Compare commits
34
Commits
3edba99d2d
...
ecb254cb54
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecb254cb54 | ||
|
|
a539b4eb71 | ||
|
|
17d991a74a | ||
|
|
977e7f996e | ||
|
|
d15b8e2f6c | ||
|
|
886a4479c4 | ||
|
|
02b87c9399 | ||
|
|
ae8c4c8c38 | ||
|
|
4706c4afeb | ||
|
|
a5cc91a92a | ||
|
|
8221a5162d | ||
|
|
a7dc1a2a1e | ||
|
|
9ca95125b8 | ||
|
|
169d9889f3 | ||
|
|
1aaed57825 | ||
|
|
b11c457e61 | ||
|
|
91cf862248 | ||
|
|
c97f6ed3bd | ||
|
|
5a76fe7521 | ||
|
|
97b4dc8508 | ||
|
|
f5df2150d8 | ||
|
|
43fe17cc70 | ||
|
|
46eff756de | ||
|
|
b6f606d9f2 | ||
|
|
af5428d0e3 | ||
|
|
c82f5e42dd | ||
|
|
e396fff790 | ||
|
|
763ea1efcc | ||
|
|
17bfa2c5fe | ||
|
|
40c56c6b33 | ||
|
|
9da9cd0a59 | ||
|
|
e690b52007 | ||
|
|
69c13284d8 | ||
|
|
f2629ccf14 |
@@ -0,0 +1,23 @@
|
||||
# 复制为根目录 .env 后再修改;根目录 .env 已加入 Git 忽略。
|
||||
|
||||
# 对外绑定地址和端口(默认仅绑定本机;局域网访问时将 COMPOSE_BIND_IP 改为 0.0.0.0)
|
||||
COMPOSE_BIND_IP=127.0.0.1
|
||||
FRONTEND_HOST_PORT=8080
|
||||
BACKEND_HOST_PORT=9099
|
||||
MYSQL_HOST_PORT=13307
|
||||
|
||||
# MySQL
|
||||
MYSQL_DATABASE=shierfeng-fastapi
|
||||
MYSQL_ROOT_PASSWORD=change-this-root-password
|
||||
DB_USERNAME=shierfeng
|
||||
DB_PASSWORD=change-this-app-password
|
||||
|
||||
# Redis 与登录令牌
|
||||
REDIS_DATABASE=2
|
||||
JWT_SECRET_KEY=replace-with-a-long-random-secret
|
||||
|
||||
# 本地 Compose 默认关闭传输层加密;正式环境启用时必须另行注入正式密钥。
|
||||
TRANSPORT_CRYPTO_ENABLED=false
|
||||
TRANSPORT_CRYPTO_MODE=off
|
||||
|
||||
TZ=Asia/Shanghai
|
||||
@@ -0,0 +1 @@
|
||||
/.env
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
../../../shierfeng-fastapi-app
|
||||
@@ -0,0 +1,152 @@
|
||||
# Task 5 交接报告
|
||||
|
||||
## Status
|
||||
|
||||
INCOMPLETE。当前尝试未完成 Task 5,未提交 commit。
|
||||
|
||||
## 已修改文件
|
||||
|
||||
本次 Task 5 尝试涉及 brief 白名单中的 18 个文件:
|
||||
|
||||
- `shierfeng-fastapi-backend/module_business/controller/inbound_controller.py`
|
||||
- `shierfeng-fastapi-backend/module_business/controller/filling_controller.py`
|
||||
- `shierfeng-fastapi-backend/module_business/controller/shipment_controller.py`
|
||||
- `shierfeng-fastapi-backend/module_business/controller/usage_controller.py`
|
||||
- `shierfeng-fastapi-backend/module_business/controller/return_controller.py`
|
||||
- `shierfeng-fastapi-backend/module_business/dao/operation_dao.py`
|
||||
- `shierfeng-fastapi-backend/module_business/dao/inventory_dao.py`
|
||||
- `shierfeng-fastapi-backend/module_business/service/inbound_service.py`
|
||||
- `shierfeng-fastapi-backend/module_business/service/filling_service.py`
|
||||
- `shierfeng-fastapi-backend/module_business/service/shipment_service.py`
|
||||
- `shierfeng-fastapi-backend/module_business/service/usage_service.py`
|
||||
- `shierfeng-fastapi-backend/module_business/service/return_service.py`
|
||||
- `shierfeng-fastapi-backend/tests/module_business/test_inbound_service.py`
|
||||
- `shierfeng-fastapi-backend/tests/module_business/test_filling_service.py`
|
||||
- `shierfeng-fastapi-backend/tests/module_business/test_shipment_service.py`
|
||||
- `shierfeng-fastapi-backend/tests/module_business/test_usage_service.py`
|
||||
- `shierfeng-fastapi-backend/tests/module_business/test_return_service.py`
|
||||
- `shierfeng-fastapi-backend/tests/module_business/test_inventory_dao.py`
|
||||
|
||||
## RED 证据
|
||||
|
||||
初始聚焦命令运行五类业务 service 与 inventory DAO 测试,共 282 个测试,结果为 `38 failures`。该 RED 结果用于确认显式 scope 契约、批次锁首语句、派生记录继承和 DAO scope 隔离的缺口。
|
||||
|
||||
## 阶段性结果
|
||||
|
||||
随后按单文件继续迁移测试调用和断言,当前结果为:
|
||||
|
||||
- `test_inbound_service`:55 tests,`24 failures / 9 errors`
|
||||
- `test_return_service`:46 tests,`16 failures / 9 errors`
|
||||
- `test_inventory_dao`:42 tests,`16 failures / 17 errors`
|
||||
|
||||
失败根因主要是既有测试中的旧 service/DAO 调用、旧参数顺序和旧 `assert_awaited_*` 断言尚未完成显式 scope 迁移;同时仍存在真实的跨 scope 校验、测试写入批次锁顺序、既有库存 ownership 保持和相关回滚行为需要继续实现与验证。生产接口当前保持 scope 必填,没有增加默认 formal scope。
|
||||
|
||||
## 未完成项
|
||||
|
||||
- 尚未使六个目标测试文件逐组 GREEN。
|
||||
- 尚未完成五条业务链路的完整跨 scope、批次锁首语句、派生 `test_batch_id` 和既有库存 ownership 验证。
|
||||
- 尚未运行 brief 要求的完整 282+ 回归测试。
|
||||
- 尚未运行最终 Ruff format/check 和 diff check。
|
||||
- 尚未执行精确白名单 `git commit --only`,当前没有可提交 SHA。
|
||||
|
||||
## 交接结论
|
||||
|
||||
这是 INCOMPLETE 交接,不代表 Task 5 完成。后续应从测试调用/断言迁移继续,修复每个单文件的真实 scope 与锁顺序失败,完成全套验证后再生成正式提交和最终报告。
|
||||
|
||||
## Task 5 最终结果(2026-09-23)
|
||||
|
||||
### Status
|
||||
|
||||
DONE。18 个白名单文件的 Task 5 实现、测试 scope 迁移和格式修复已完成;未提交 commit。生产代码保留显式 scope 必填,没有添加 formal 默认值;测试/生产数据中 `test_batch_id=NULL` 仍表示正式数据。
|
||||
|
||||
### 本轮完成
|
||||
|
||||
- 完成 `test_return_service.py` 的旧 DAO 调用、内部 helper 调用和 mock 断言迁移,补齐显式 `TestBatchScope`。
|
||||
- 完成 `test_inventory_dao.py` 的 DAO 签名期望、列表参数顺序、幂等查询、详情/锁查询和写入调用迁移。
|
||||
- 对 5 个控制器和 6 个业务测试文件执行白名单范围内的 Ruff 导入/格式修复;未修改白名单之外的源码、测试、日志或 pycache。
|
||||
- 复核生产实现:DAO 根查询及明细查询应用 `batch_scope_condition`;测试写入口首条数据库语句锁定 active batch;派生订单、明细和事件显式继承 `test_batch_id`;已有库存未被改写 ownership;入库/发货旧 DELETE 在访问 DAO 前固定抛出批次清理提示;授权、状态机、幂等、锁顺序和回滚回归通过。
|
||||
|
||||
### 验证命令与真实结果
|
||||
|
||||
1. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_inbound_service`:`Ran 55 tests ... OK`。
|
||||
2. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_filling_service`:`Ran 42 tests ... OK`。
|
||||
3. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_shipment_service`:`Ran 46 tests ... OK`。
|
||||
4. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_usage_service`:`Ran 51 tests ... OK`。
|
||||
5. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_return_service`:`Ran 46 tests ... OK`。
|
||||
6. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_inventory_dao`:`Ran 42 tests ... OK`。
|
||||
7. 六文件合并命令:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_inbound_service tests.module_business.test_filling_service tests.module_business.test_shipment_service tests.module_business.test_usage_service tests.module_business.test_return_service tests.module_business.test_inventory_dao -v`:`Ran 282 tests ... OK`。
|
||||
8. 白名单 `ruff format --check`:`18 files already formatted`,exit 0。
|
||||
9. 白名单 `ruff check`:`All checks passed!`,exit 0。
|
||||
10. 目录级 `ruff check module_business/controller module_business/dao module_business/service tests/module_business`:`All checks passed!`,exit 0。
|
||||
11. 白名单范围 `git diff --check -- <18 files>`:无输出,exit 0。
|
||||
12. 全仓 `git diff --check`:exit 2;首个实际根因为用户已有 `shierfeng-fastapi-backend/logs/2026/09/20/info.log` 的 trailing whitespace,未修改日志。
|
||||
|
||||
### Concerns
|
||||
|
||||
- 目录级 `ruff format --check module_business/controller module_business/dao module_business/service tests/module_business` 仍 exit 1,精确列出 4 个非白名单文件:`module_business/dao/test_batch_dao.py`、`module_business/service/test_batch_service.py`、`tests/module_business/test_sql_contract.py`、`tests/module_business/test_test_batch_service.py`。受本任务 18 文件白名单约束,未修改。
|
||||
- 全仓 `git diff --check` 仍被用户已有日志 trailing whitespace 阻断;Task 5 白名单范围检查已通过。
|
||||
|
||||
## 停止长任务交接(2026-09-23)
|
||||
|
||||
本次停止请求已立即停止后续工作;当前没有仍在运行的测试或命令,没有回滚工作区改动,也未提交 commit。
|
||||
|
||||
本次停止请求本身未启动新命令,因此没有新增命令输出。停止前最近一次真实验证结果保持不变:六个单文件测试分别为 `55/55`、`42/42`、`46/46`、`51/51`、`46/46`、`42/42`,合并回归为 `Ran 282 tests ... OK`;白名单 Ruff format/check 和白名单 `git diff --check` 均 exit 0。
|
||||
|
||||
剩余阻塞/concerns:
|
||||
|
||||
- 目录级 Ruff format 仍因 4 个非白名单文件未格式化而 exit 1:`module_business/dao/test_batch_dao.py`、`module_business/service/test_batch_service.py`、`tests/module_business/test_sql_contract.py`、`tests/module_business/test_test_batch_service.py`。
|
||||
- 全仓 `git diff --check` 仍因用户已有 `shierfeng-fastapi-backend/logs/2026/09/20/info.log` trailing whitespace 而 exit 2;未修改该日志。
|
||||
|
||||
## Task 5 Fix Round 1(2026-09-23)
|
||||
|
||||
### Status
|
||||
|
||||
DONE_WITH_CONCERNS。review 指出的生产缺陷已修复,并创建了退回申请 `IntegrityError` 入口回归和事件历史指针 scope SQL 回归;未执行真实数据库写入或迁移。
|
||||
|
||||
### 修复内容
|
||||
|
||||
- `ReturnService.request_return_services` 现在向 `_recover_request_integrity_error` 传入 `test_scope`;入口回归通过真实恢复分支验证同一 TEST batch 的幂等赢家稳定返回。
|
||||
- 六个 `IntegrityError` recovery helper 在任何授权或业务查询前调用 `lock_active_batch`;退回申请的 site 重授权 helper 不再在 site 行锁后锁 batch。退回申请、确认及使用在预读 rollback 后开始的新事务也先重锁 batch。
|
||||
- `InventoryDao.add_event` 现在强制接收 `scope`,历史 `last_event_id` 查询加入 `batch_scope_condition(BizPalletEvent.test_batch_id, scope)`;五个服务的全部事件写入调用均传递当前 scope。
|
||||
- 入库和发货 DELETE OpenAPI summary/description 更新为固定拒绝消息:`不支持单条物理删除,请通过测试数据批次清理`。
|
||||
- 业务测试中的事件 DAO 替身更新为四参 API。
|
||||
|
||||
### TDD 与验证命令(真实输出摘要)
|
||||
|
||||
1. RED:`cd shierfeng-fastapi-backend && PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_return_service.ReturnRequestTransactionRedTest.test_request_integrity_error_recovers_same_test_batch_idempotently -v`:exit 1,`TypeError: ReturnService._recover_request_integrity_error() missing 1 required positional argument: 'test_scope'`。
|
||||
2. RED:`cd shierfeng-fastapi-backend && PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_inventory_dao.TestDaoPublicContract.test_inventory_dao_critical_parameter_names_are_stable tests.module_business.test_inventory_dao.TestInventoryDao.test_add_event_reads_exact_previous_pointer_and_updates_inventory -v`:exit 1,`add_event` 缺少 `scope` 参数,调用报 `takes 4 positional arguments but 5 were given`。
|
||||
3. GREEN:上述退回入口单测:`Ran 1 test in 0.023s`,`OK`。
|
||||
4. GREEN:`TestDaoPublicContract.test_inventory_dao_critical_parameter_names_are_stable` 及三个 `add_event` DAO 单测:`Ran 4 tests in 0.021s`,`OK`。
|
||||
5. 单文件:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_inbound_service -v`:`Ran 55 tests ... OK`。
|
||||
6. 单文件:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_filling_service -v`:`Ran 42 tests ... OK`。
|
||||
7. 单文件:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_shipment_service -v`:`Ran 46 tests ... OK`。
|
||||
8. 单文件:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_usage_service -v`:`Ran 51 tests ... OK`。
|
||||
9. 单文件:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_return_service -v`:`Ran 47 tests ... OK`。
|
||||
10. 单文件:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_inventory_dao -v`:`Ran 42 tests ... OK`。
|
||||
11. 合并:`PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m unittest tests.module_business.test_inbound_service tests.module_business.test_filling_service tests.module_business.test_shipment_service tests.module_business.test_usage_service tests.module_business.test_return_service tests.module_business.test_inventory_dao -v`:`Ran 283 tests in 1.761s`,`OK`。计数为 283 是本轮新增退回恢复回归的结果。
|
||||
12. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m ruff format --check <18 个白名单文件>`:`18 files already formatted`,exit 0。
|
||||
13. `PYTHONDONTWRITEBYTECODE=1 conda run -n ShiErFeng python -m ruff check <18 个白名单文件>`:`All checks passed!`,exit 0。
|
||||
14. `git diff --check -- <18 个白名单文件及本报告>`:无输出,exit 0(在追加本段前执行;追加后会在提交前重新执行)。
|
||||
|
||||
### Concerns
|
||||
|
||||
- 本轮新增的 TEST scope 入口成功回归目前集中于退回申请;入库、装填、发货和使用的既有业务成功回归仍以 `TestBatchScope.formal()` 为主。生产路径、DAO SQL scope 条件和六文件回归均已验证,但尚未为这四条链路分别补齐 TEST batch 成功、跨批次拒绝、CLOSED 拒绝、派生字段和 rollback 的专门用例。
|
||||
- 目录级 Ruff format 和全仓 `git diff --check` 的既有非白名单阻塞仍按上文保留,未修改用户文件。
|
||||
|
||||
## Task 5 接管后补充(2026-09-23)
|
||||
|
||||
### 本轮修复
|
||||
|
||||
- 为正式模式 `SimpleNamespace` 仓库替身补齐 `test_batch_id=None`,保留服务层对已加载对象的强制 scope 校验。
|
||||
- 入库、装填、发货扫码路径对仓库/现场、托盘、库存和活动装填记录增加显式 `assert_record_in_scope` 防御校验;不放宽 DAO 过滤。
|
||||
- 装填与发货各增加 TEST scope 跨批次写入回滚回归;装填与发货扫码各增加跨批次对象拒绝回归。
|
||||
|
||||
### 最新验证
|
||||
|
||||
1. 红测:装填与发货跨批次扫码用例在新增服务校验前分别以 `ServiceException not raised` 失败;两条写入用例同时确认回滚路径可用。
|
||||
2. 绿测:四条新增回归:`Ran 4 tests ... OK`。
|
||||
3. 六文件合并:`PYTHONDONTWRITEBYTECODE=1 OPENBLAS_NUM_THREADS=1 OMP_NUM_THREADS=1 /Users/jiaolongyan/miniconda3/envs/ShiErFeng/bin/python -u -m unittest tests.module_business.test_inbound_service tests.module_business.test_filling_service tests.module_business.test_shipment_service tests.module_business.test_usage_service tests.module_business.test_return_service tests.module_business.test_inventory_dao -v`:`Ran 290 tests ... OK`。
|
||||
4. 18 文件 `ruff format --check`:`18 files already formatted`;`ruff check`:`All checks passed!`。
|
||||
5. 18 文件范围 `git diff --check`:无输出,exit 0。
|
||||
|
||||
当前 Task 5 代码已具备提交条件;本报告仍只保留为本地审查材料,不应纳入业务提交。
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
* 前端采用Vue3、Element Plus。
|
||||
* 移动端采用uni-app、Vue3、Vite,内置tailwindcss。
|
||||
* 后端采用FastAPI、sqlalchemy、MySQL(PostgreSQL)、Redis、OAuth2 & Jwt。
|
||||
* 后端采用FastAPI、sqlalchemy、MySQL、Redis、OAuth2 & Jwt。
|
||||
* 权限认证使用OAuth2 & Jwt,支持多终端认证系统。
|
||||
* 支持加载动态权限菜单,多方式轻松权限控制。
|
||||
* 纯Python版本:
|
||||
@@ -43,14 +43,16 @@
|
||||
* 基础资料:菌种、客户、客户现场、产线、仓库、托盘。
|
||||
* 业务流程:仓库入库、工厂装填、发货出库、客户现场使用、退回仓库。
|
||||
* 只读报表:托盘库存、装填记录、发货出库、产线消耗、退回仓库、托盘追溯。
|
||||
* Android PDA 可直接通过浏览器使用响应式 Web 页面,并支持 HID 扫码枪扫码后回车提交。
|
||||
* Android PDA 可通过基于 uni-app 的 H5 页面使用,并支持 HID 扫码枪扫码后回车提交。
|
||||
|
||||
本轮交付不是 APP/uni-app 原生端,也未调用设备摄像头。上线前请阅读[托盘流转业务部署说明](./docs/pallet-workflow-deployment.md)。
|
||||
本轮交付包含 PDA H5 页面,但暂不打包 APK 或其他原生安装包,也未调用设备摄像头。上线前请阅读[托盘流转业务部署说明](./docs/pallet-workflow-deployment.md)。
|
||||
|
||||
测试数据批次的创建、授权、历史数据纳入和清理流程请阅读[测试数据批次操作说明](./docs/test-data-batch-operations.md)。
|
||||
|
||||
## 在线体验
|
||||
|
||||
* *账号:admin*
|
||||
* *密码:admin123*
|
||||
* *密码:请通过授权渠道获取,不在仓库中记录*
|
||||
* 演示地址:<a href="https://vfadmin.insistence.tech">vfadmin管理系统<a>
|
||||
|
||||
## 项目开发及发布相关
|
||||
@@ -110,17 +112,15 @@ pnpm dev:mp-weixin
|
||||
# 进入后端目录
|
||||
cd shierfeng-fastapi-backend
|
||||
|
||||
# 如果使用的是MySQL数据库,请执行以下命令安装项目依赖环境
|
||||
# 安装项目依赖环境(统一使用 MySQL)
|
||||
pip3 install -r requirements.txt
|
||||
# 如果使用的是PostgreSQL数据库,请执行以下命令安装项目依赖环境
|
||||
pip3 install -r requirements-pg.txt
|
||||
|
||||
# 配置环境
|
||||
在.env.dev文件中配置开发环境的数据库和redis
|
||||
|
||||
# 运行sql文件
|
||||
1.新建数据库shierfeng-fastapi(默认,可修改)
|
||||
2.如果使用的是MySQL数据库,使用命令或数据库连接工具运行sql文件夹下的shierfeng-fastapi.sql;如果使用的是PostgreSQL数据库,使用命令或数据库连接工具运行sql文件夹下的shierfeng-fastapi-pg.sql
|
||||
# 运行 SQL 文件
|
||||
1. 新建数据库 shierfeng-fastapi(默认,可修改)
|
||||
2. 使用命令或数据库连接工具运行 sql 文件夹下的 shierfeng-fastapi.sql
|
||||
|
||||
# 运行后端
|
||||
shierfeng app run --env=dev
|
||||
@@ -131,12 +131,12 @@ shierfeng app run --env=dev
|
||||
#### 访问
|
||||
|
||||
```bash
|
||||
# 默认账号密码
|
||||
# 本地账号
|
||||
账号:admin
|
||||
密码:admin123
|
||||
密码:请在目标环境安全配置中设置后手工输入,不在仓库中记录
|
||||
|
||||
# 浏览器访问
|
||||
地址:http://localhost:80
|
||||
地址:http://localhost:8080
|
||||
```
|
||||
|
||||
### 发布
|
||||
@@ -163,16 +163,25 @@ shierfeng app run --env=prod
|
||||
|
||||
### Docker Compose部署方式
|
||||
|
||||
> ⚠️ **警告:** 默认未做数据持久化配置,请注意数据备份或自行配置持久化
|
||||
> ⚠️ **说明:** 根目录 Compose 使用 MySQL 和 Redis 命名卷持久化数据。初始化 SQL 只会在 MySQL 数据目录首次为空时执行。
|
||||
|
||||
#### MySQL版本
|
||||
#### MySQL 全栈版本
|
||||
|
||||
```bash
|
||||
docker compose -f shierfeng-fastapi-test/docker-compose.test.my.yml up -d --build
|
||||
# 首次使用时创建本地配置文件
|
||||
cp .env.docker.example .env
|
||||
# 编辑 .env,至少修改 MYSQL_ROOT_PASSWORD、DB_PASSWORD 和 JWT_SECRET_KEY
|
||||
|
||||
# 启动前只做静态配置校验
|
||||
docker compose config --quiet
|
||||
|
||||
# 构建并启动前端、后端、MySQL、Redis
|
||||
docker compose up -d --build
|
||||
|
||||
# 查看服务状态
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
#### PostgreSQL版本
|
||||
浏览器访问:http://localhost:8080;后端直连接口为 `http://localhost:9099`;宿主机连接 MySQL 时使用 `127.0.0.1:13307`。需要局域网访问时,将根目录 `.env` 中的 `COMPOSE_BIND_IP` 改为 `0.0.0.0`,再使用宿主机局域网地址访问前端和后端端口。
|
||||
|
||||
```bash
|
||||
docker compose -f shierfeng-fastapi-test/docker-compose.test.pg.yml up -d --build
|
||||
```
|
||||
已有数据库不能通过全量初始化脚本升级。请先备份并核对目标库,再按照 `shierfeng-fastapi-backend/sql/patches/` 下对应的 MySQL 增量补丁和部署文档执行;不要通过删除 Compose 数据卷来代替迁移。
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
services:
|
||||
shierfeng-mysql:
|
||||
image: mysql:8.0
|
||||
container_name: shierfeng-mysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?请在 .env 中设置 MYSQL_ROOT_PASSWORD}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-shierfeng-fastapi}
|
||||
MYSQL_USER: ${DB_USERNAME:-shierfeng}
|
||||
MYSQL_PASSWORD: ${DB_PASSWORD:?请在 .env 中设置 DB_PASSWORD}
|
||||
TZ: ${TZ:-Asia/Shanghai}
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_general_ci
|
||||
- --skip-character-set-client-handshake=1
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
- ./shierfeng-fastapi-backend/sql/shierfeng-fastapi.sql:/docker-entrypoint-initdb.d/01-shierfeng-fastapi.sql:ro
|
||||
ports:
|
||||
- ${COMPOSE_BIND_IP:-127.0.0.1}:${MYSQL_HOST_PORT:-13307}:3306
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- >-
|
||||
mysqladmin ping -h 127.0.0.1 -uroot
|
||||
-p"$${MYSQL_ROOT_PASSWORD}" --silent
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 20s
|
||||
networks:
|
||||
- shierfeng-network
|
||||
|
||||
shierfeng-redis:
|
||||
image: redis:7-alpine
|
||||
container_name: shierfeng-redis
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- redis-server
|
||||
- --appendonly
|
||||
- 'yes'
|
||||
- --save
|
||||
- '60'
|
||||
- '1000'
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: [CMD, redis-cli, ping]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 5s
|
||||
networks:
|
||||
- shierfeng-network
|
||||
|
||||
shierfeng-backend-my:
|
||||
build:
|
||||
context: ./shierfeng-fastapi-backend
|
||||
dockerfile: Dockerfile.my
|
||||
image: shierfeng-backend-my:latest
|
||||
container_name: shierfeng-backend-my
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
APP_ENV: dockermy
|
||||
APP_HOST: 0.0.0.0
|
||||
APP_PORT: '9099'
|
||||
APP_ROOT_PATH: /docker-api
|
||||
APP_RELOAD: 'false'
|
||||
APP_WORKERS: '1'
|
||||
APP_DEMO_MODE: 'false'
|
||||
APP_DISABLE_SWAGGER: 'true'
|
||||
APP_DISABLE_REDOC: 'true'
|
||||
DB_TYPE: mysql
|
||||
DB_HOST: shierfeng-mysql
|
||||
DB_PORT: '3306'
|
||||
DB_USERNAME: ${DB_USERNAME:-shierfeng}
|
||||
DB_PASSWORD: ${DB_PASSWORD:?请在 .env 中设置 DB_PASSWORD}
|
||||
DB_DATABASE: ${MYSQL_DATABASE:-shierfeng-fastapi}
|
||||
DB_ECHO: 'false'
|
||||
REDIS_HOST: shierfeng-redis
|
||||
REDIS_PORT: '6379'
|
||||
REDIS_USERNAME: ${REDIS_USERNAME:-}
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
|
||||
REDIS_DATABASE: ${REDIS_DATABASE:-2}
|
||||
JWT_SECRET_KEY: ${JWT_SECRET_KEY:?请在 .env 中设置 JWT_SECRET_KEY}
|
||||
TRANSPORT_CRYPTO_ENABLED: ${TRANSPORT_CRYPTO_ENABLED:-false}
|
||||
TRANSPORT_CRYPTO_MODE: ${TRANSPORT_CRYPTO_MODE:-off}
|
||||
LOG_FILE_ENABLED: 'false'
|
||||
LOGURU_STDOUT: 'true'
|
||||
expose:
|
||||
- '9099'
|
||||
ports:
|
||||
- ${COMPOSE_BIND_IP:-127.0.0.1}:${BACKEND_HOST_PORT:-9099}:9099
|
||||
depends_on:
|
||||
shierfeng-mysql:
|
||||
condition: service_healthy
|
||||
shierfeng-redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- >-
|
||||
python -c "import socket; s=socket.create_connection(('127.0.0.1', 9099), 2); s.close()"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 20s
|
||||
networks:
|
||||
- shierfeng-network
|
||||
|
||||
shierfeng-frontend:
|
||||
build:
|
||||
context: ./shierfeng-fastapi-frontend
|
||||
dockerfile: Dockerfile
|
||||
image: shierfeng-frontend:latest
|
||||
container_name: shierfeng-frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- ${COMPOSE_BIND_IP:-127.0.0.1}:${FRONTEND_HOST_PORT:-8080}:80
|
||||
volumes:
|
||||
- ./shierfeng-fastapi-frontend/bin/nginx.dockermy.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
shierfeng-backend-my:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- shierfeng-network
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
redis-data:
|
||||
|
||||
networks:
|
||||
shierfeng-network:
|
||||
name: shierfeng-network
|
||||
driver: bridge
|
||||
@@ -0,0 +1,106 @@
|
||||
# PDA 托盘业务验收与培训手册
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本手册用于服务器、数据库和 PDA 设备准备完成后的现场验收与客户培训。它不替代部署、数据库迁移或 APK 打包,也不把本地构建结果视为真机验收结果。
|
||||
|
||||
当前代码交付物是 uni-app PDA 页面、接口封装、HID 扫码组件和可验证 H5 构建;APK 按当前范围暂不生成。
|
||||
|
||||
## 2. 验收前置条件
|
||||
|
||||
- 后端服务已部署并可从 PDA 访问,已确定 `VITE_APP_BASE_API` 对应地址。
|
||||
- 数据库已执行与目标数据库类型匹配的初始化 SQL/业务补丁,并完成备份。
|
||||
- Redis、JWT、传输加密和验证码配置已按目标环境确认。
|
||||
- 已准备管理员、仓库操作员、客户现场操作员三类账号,并分别配置仓库/现场数据范围。
|
||||
- 已准备至少两个空托盘、一个已装填托盘、一个可部分使用托盘和一个可退回托盘;每个托盘二维码唯一且可扫描。
|
||||
- PDA 与扫码头处于 HID 键盘输入模式,扫码结束符配置为回车;网络稳定性和离线恢复策略已确认。
|
||||
- 验收记录表包含:用例编号、账号、托盘码、时间、预期结果、实际结果、截图/日志、问题编号和签字人。
|
||||
|
||||
## 3. 角色验收
|
||||
|
||||
| 角色 | 应能操作 | 不应能操作 |
|
||||
| --- | --- | --- |
|
||||
| 管理员 | 在授权范围内查看基础资料、业务单、报表和追溯 | 不应绕过后端权限与数据范围 |
|
||||
| 仓库操作员 | 入库、装填、发货、退回收货 | 不应操作未授权仓库或客户现场 |
|
||||
| 客户现场操作员 | 扫码查看托盘、绑定产线、录入使用、发起退回 | 不应确认仓库收货或访问未授权现场 |
|
||||
|
||||
登录页默认填充用户名 `admin`;密码必须由操作员现场手工输入,不记录在测试单、日志或源码中。
|
||||
|
||||
## 4. 核心业务验收用例
|
||||
|
||||
### 4.1 基础资料与二维码
|
||||
|
||||
1. Web 后台新增菌种、客户、现场、产线、仓库和托盘。
|
||||
2. 为托盘设置容量,生成并查看固定二维码。
|
||||
3. 重复托盘编号或二维码应被拒绝;停用托盘不能进入业务流程。
|
||||
4. 为仓库和现场分配用户,使用受限账号确认数据范围。
|
||||
|
||||
### 4.2 入库
|
||||
|
||||
1. 仓库用户选择授权仓库,扫描 `UNSTOCKED` 托盘。
|
||||
2. 重复扫描同一托盘应被前端提示并不会重复加入。
|
||||
3. 创建入库草稿,移除一条明细后保存并重新进入详情。
|
||||
4. 确认入库,检查库存状态变为仓库空托盘,并检查托盘事件。
|
||||
5. 在响应丢失后使用同一操作重试,确认不会重复入库。
|
||||
|
||||
### 4.3 装填
|
||||
|
||||
1. 扫描仓库空托盘,确认页面显示仓库、容量和当前余量。
|
||||
2. 选择菌种,录入批次、规格和正整数包数。
|
||||
3. 使用 0、负数、超容量和未授权仓库数据验证后端拒绝。
|
||||
4. 提交成功后检查状态、菌种、批次、初始包数和装填事件。
|
||||
|
||||
### 4.4 发货
|
||||
|
||||
1. 选择授权来源仓库和目标客户现场,连续扫描可发货托盘。
|
||||
2. 检查页面展示的菌种、批次和剩余包数为服务端数据。
|
||||
3. 创建草稿并确认,检查来源库存扣减、现场位置和发货事件。
|
||||
4. 用同一幂等键重试确认,确认不会重复扣减库存。
|
||||
|
||||
### 4.5 客户现场使用
|
||||
|
||||
1. 现场用户扫描已发货托盘,确认菌种、批次和剩余包数可读。
|
||||
2. 首次使用选择当前现场产线,录入正整数使用包数。
|
||||
3. 使用小于余量的包数,确认状态为部分使用且剩余包数正确。
|
||||
4. 使用剩余全部包数,确认状态为现场空托盘且剩余包数为 0。
|
||||
5. 尝试切换已绑定的其他产线、超出余量或使用 0 包,确认请求被拒绝。
|
||||
|
||||
### 4.6 退回
|
||||
|
||||
1. 现场用户选择目标仓库并扫描可退回托盘,提交退回申请。
|
||||
2. 检查托盘状态进入退回中,不能再次发货或重复申请。
|
||||
3. 仓库用户查看退回详情,扫描整集合托盘并确认收货。
|
||||
4. 漏扫、错扫、重复扫和余量快照变化均应阻止确认。
|
||||
5. 确认成功后检查仓库库存、托盘状态和退回事件。
|
||||
|
||||
## 5. 报表、权限与异常验收
|
||||
|
||||
- 查看托盘库存、装填、发货、产线消耗、退回和托盘追溯六类报表。
|
||||
- 用受限账号验证未授权菜单不显示,直接调用接口返回 403。
|
||||
- 检查关键写操作包含操作人、时间、业务单和托盘事件。
|
||||
- 在扫码请求期间连续回车,确认输入锁生效且不会并发提交。
|
||||
- 断网后重试,确认使用原幂等键;网络恢复后不会重复扣减库存。
|
||||
- 使用非法状态、库存不足、错误仓库/现场和旧详情快照,确认错误可见且状态不被部分写入。
|
||||
|
||||
## 6. 培训顺序
|
||||
|
||||
1. 讲解托盘二维码、状态和“服务端结果为准”原则。
|
||||
2. 仓库操作员练习入库、装填、发货和退回收货。
|
||||
3. 客户现场操作员练习扫码、产线选择、部分/全部使用和退回申请。
|
||||
4. 管理员练习基础资料、用户数据范围、报表和托盘追溯。
|
||||
5. 演练重复扫码、断网重试、无权限和库存不足,不使用真实业务数据做首次演示。
|
||||
6. 培训结束后由各角色完成一条完整闭环:入库 → 装填 → 发货 → 使用 → 退回。
|
||||
|
||||
## 7. 当前不包含事项
|
||||
|
||||
- 本阶段不打包、签名、安装或发布 APK。
|
||||
- 本阶段不调用摄像头扫码,不新增原生扫码 SDK;扫码方式为 HID 扫码头加手工输入兜底。
|
||||
- 本手册不执行生产部署、数据库迁移、密码重置或客户培训;这些动作需在目标环境中按前置条件执行并留存记录。
|
||||
|
||||
## 8. 代码证据
|
||||
|
||||
- 设计与需求覆盖矩阵:`docs/superpowers/specs/2026-09-20-pda-business-workflow-design.md`
|
||||
- PDA 页面与接口:`shierfeng-fastapi-app/src/pages/business`、`shierfeng-fastapi-app/src/api/business`
|
||||
- Web 基础资料、业务和报表:`shierfeng-fastapi-frontend/src/views`
|
||||
- 后端业务与状态机:`shierfeng-fastapi-backend/module_business`
|
||||
- 数据库结构:`shierfeng-fastapi-backend/sql`
|
||||
@@ -0,0 +1,721 @@
|
||||
# PDA 托盘业务操作功能 Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 在不改变 FastAPI 业务契约的前提下,为 `shierfeng-fastapi-app` 增加可在 H5/开发环境验证的托盘入库、装填、发货、现场使用和退回操作。
|
||||
|
||||
**Architecture:** PDA 复用现有 `request`、JWT、传输加密和后端业务接口;API 封装按业务模块拆分,扫码输入和幂等键生成作为无业务状态的共享工具。五个页面各自维护表单、扫码会话、请求代数和服务端刷新状态,后端继续作为权限、库存、状态机、产线绑定和退回集合校验的唯一权威。
|
||||
|
||||
**Tech Stack:** uni-app Vue 3、Pinia、Tailwind CSS、现有 `src/utils/request.js`、Node 20 `node:test`、pnpm;不增加依赖。
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-09-20-pda-business-workflow-design.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- 不打包、签名、安装或发布 APK;只运行 PDA 的 H5/开发构建。
|
||||
- 不调用摄像头扫码,不新增原生扫码 SDK;扫码输入必须兼容 HID 键盘逐字符输入后回车提交,并保留手工输入。
|
||||
- 不修改现有 FastAPI 业务接口的请求/响应契约,不在 PDA 前端计算最终库存或托盘状态。
|
||||
- 基础资料维护和六类报表继续使用 Web 后台,不复制到 PDA。
|
||||
- 不把管理员密码、Token、Cookie 或其他凭据写入源码、测试固定文本或日志;登录表单不得预填密码。
|
||||
- 不修改 `.env.dev`、`.env.prod`、前端环境文件、用户已有 APP 改动、后端日志和无关缓存。
|
||||
- 不安装、升级或删除依赖;使用仓库已有 `node_modules`、Node 20 和 pnpm 配置。
|
||||
- 所有业务写请求必须使用后端要求的 camelCase 字段和幂等键;网络失败后直接重试复用原键,改变业务输入后才生成新键。
|
||||
- 页面入口只按当前用户权限显示;页面内按钮权限与后端 401/403 校验同时保留,前端隐藏不能替代后端权限。
|
||||
- 每个提交只包含本任务明确列出的文件;暂存和提交使用显式路径,禁止 `git add -A`、`git clean`、重置或覆盖用户已有改动。
|
||||
- 每个任务完成后使用中文提交说明,并用 `git commit --only` 保护工作区中既有暂存内容。
|
||||
|
||||
---
|
||||
|
||||
## 文件结构与责任边界
|
||||
|
||||
### 新建文件
|
||||
|
||||
- `shierfeng-fastapi-app/src/api/business/inbound.js`:首次入库列表、选项、扫码、详情、草稿增改删和确认请求。
|
||||
- `shierfeng-fastapi-app/src/api/business/filling.js`:装填列表、菌种选项、扫码、详情和装填提交请求。
|
||||
- `shierfeng-fastapi-app/src/api/business/shipment.js`:发货列表、仓库/现场选项、扫码、详情、草稿增改删和确认请求。
|
||||
- `shierfeng-fastapi-app/src/api/business/usage.js`:使用列表、产线选项、扫码、详情和使用提交请求。
|
||||
- `shierfeng-fastapi-app/src/api/business/return.js`:退回列表、仓库选项、扫码、申请、详情和确认请求。
|
||||
- `shierfeng-fastapi-app/src/utils/palletScan.js`:扫码值规范化、短时重复判断和 Web Crypto UUID v4 幂等键生成;不保存业务状态。
|
||||
- `shierfeng-fastapi-app/src/components/business/PalletScanInput.vue`:HID/手工输入、回车提交、空值校验、短时重复提示和输入焦点管理。
|
||||
- `shierfeng-fastapi-app/src/pages/business/inbound/index.vue`:入库草稿编辑、扫码、删除和确认。
|
||||
- `shierfeng-fastapi-app/src/pages/business/filling/index.vue`:单托盘装填。
|
||||
- `shierfeng-fastapi-app/src/pages/business/shipment/index.vue`:发货草稿编辑、扫码、删除和确认。
|
||||
- `shierfeng-fastapi-app/src/pages/business/usage/index.vue`:现场使用、产线选择和服务端结果展示。
|
||||
- `shierfeng-fastapi-app/src/pages/business/return/index.vue`:退回申请和仓库确认两个独立操作区。
|
||||
- `shierfeng-fastapi-app/tests/business-contract.test.mjs`:Node 20 静态 API/页面/字段/安全契约与扫码工具测试。
|
||||
|
||||
### 修改文件
|
||||
|
||||
- `shierfeng-fastapi-app/package.json`:增加不依赖新包的 `test:business` 脚本。
|
||||
- `shierfeng-fastapi-app/src/pages.json`:注册五个业务页面路由。
|
||||
- `shierfeng-fastapi-app/src/pages/login.vue`:移除旧默认密码预填值,保留现有认证流程。
|
||||
- `shierfeng-fastapi-app/src/pages/work/index.vue`:增加按权限过滤的五类 PDA 业务入口。
|
||||
|
||||
### 明确不修改
|
||||
|
||||
- `shierfeng-fastapi-backend/module_business/**`:后端接口已存在,本计划只消费其当前契约。
|
||||
- `shierfeng-fastapi-frontend/**`:Web 端作为接口和交互参考,不复制改动。
|
||||
- `shierfeng-fastapi-app/src/utils/request.js`、传输加密、Pinia 用户会话和环境配置:除非测试证明共享层存在直接阻塞,否则保持现状。
|
||||
|
||||
## 已核实的后端接口与字段
|
||||
|
||||
| 模块 | 查询/选项/扫码 | 写请求 | 必须保持的写入字段 |
|
||||
| --- | --- | --- | --- |
|
||||
| 入库 | `GET /business/inbound/list`、`/warehouse-options`、`/scan/{qrToken}?warehouseId=`、`/{inboundId}` | `POST /business/inbound`、`PUT /business/inbound`、`DELETE /business/inbound/{ids}`、`PUT /business/inbound/{id}/confirm` | 草稿:`warehouseId,palletIds`;确认:`idempotencyKey` |
|
||||
| 装填 | `GET /business/filling/list`、`/strain-options`、`/scan/{qrToken}`、`/{fillingId}` | `POST /business/filling` | `qrToken,warehouseId,strainId,batchNo,packageSpec,initialPackages,idempotencyKey` |
|
||||
| 发货 | `GET /business/shipment/list`、`/warehouse-options`、`/site-options`、`/scan/{qrToken}?sourceWarehouseId=&targetSiteId=`、`/{shipmentId}` | `POST /business/shipment`、`PUT /business/shipment`、`DELETE /business/shipment/{ids}`、`PUT /business/shipment/{id}/confirm` | 草稿:`sourceWarehouseId,targetSiteId,palletIds`;确认:`idempotencyKey` |
|
||||
| 使用 | `GET /business/usage/list`、`/line-options?siteId=`、`/scan/{qrToken}`、`/{usageId}` | `POST /business/usage` | `qrToken,lineId,usedPackages,idempotencyKey` |
|
||||
| 退回 | `GET /business/return/list`、`/warehouse-options`、`/scan/{qrToken}?mode=&returnId=`、`/{returnId}` | `POST /business/return/request`、`PUT /business/return/{id}/confirm` | 申请:`sourceSiteId,targetWarehouseId,palletIds,idempotencyKey`;确认:`scannedPalletIds,idempotencyKey` |
|
||||
|
||||
后端扫码响应统一使用 `data.palletId/palletCode/flowState/warehouseId/siteId/currentLineId/strainId/strainCode/strainName/batchNo/packageSpec/capacityPackages/remainingPackages/allowedActions`;列表响应使用 `rows` 和 `total`;详情使用 `data`。装填和使用写请求没有单独确认接口,成功后以列表、扫码结果或使用响应为准。
|
||||
|
||||
## Task 1: 用测试锁定 API 封装、扫码工具和幂等键
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
- Create: `shierfeng-fastapi-app/src/api/business/inbound.js`
|
||||
- Create: `shierfeng-fastapi-app/src/api/business/filling.js`
|
||||
- Create: `shierfeng-fastapi-app/src/api/business/shipment.js`
|
||||
- Create: `shierfeng-fastapi-app/src/api/business/usage.js`
|
||||
- Create: `shierfeng-fastapi-app/src/api/business/return.js`
|
||||
- Create: `shierfeng-fastapi-app/src/utils/palletScan.js`
|
||||
- Modify: `shierfeng-fastapi-app/package.json`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Produces API exports `listInbound`, `getInboundWarehouseOptions`, `scanInboundPallet`, `getInbound`, `addInbound`, `updateInbound`, `delInbound`, `confirmInbound`;其他四个模块使用已核实接口表中的同义导出名。
|
||||
- Produces `normalizeScanValue(value)`, `isDuplicateScan(value,lastValue,scannedAt,lastScannedAt,duplicateWindowMs)` 和 `createIdempotencyKey(cryptoProvider)`。
|
||||
- All API wrappers import `request` from `@/utils/request` and return its promise without改写响应结构。
|
||||
|
||||
各模块的完整导出名固定如下,页面不得自行拼接 URL:
|
||||
|
||||
- `inbound.js`:`listInbound(query)`、`getInboundWarehouseOptions()`、`scanInboundPallet(qrToken,query)`、`getInbound(inboundId)`、`addInbound(data)`、`updateInbound(data)`、`delInbound(inboundIds)`、`confirmInbound(inboundId,data)`。
|
||||
- `filling.js`:`listFilling(query)`、`getFillingStrainOptions()`、`scanFillingPallet(qrToken)`、`getFilling(fillingId)`、`addFilling(data)`。
|
||||
- `shipment.js`:`listShipment(query)`、`getShipmentWarehouseOptions()`、`getShipmentSiteOptions()`、`scanShipmentPallet(qrToken,query)`、`getShipment(shipmentId)`、`addShipment(data)`、`updateShipment(data)`、`delShipment(shipmentIds)`、`confirmShipment(shipmentId,data)`。
|
||||
- `usage.js`:`listUsage(query)`、`getUsageLineOptions(siteId)`、`scanUsagePallet(qrToken)`、`getUsage(usageId)`、`addUsage(data)`。
|
||||
- `return.js`:`listReturn(query)`、`getReturnWarehouseOptions()`、`scanReturnPallet(qrToken,query)`、`requestReturn(data)`、`getReturn(returnId)`、`confirmReturn(returnId,data)`。
|
||||
|
||||
- [ ] **Step 1: 先写 API 与工具的失败测试**
|
||||
|
||||
在测试文件中用 `readFile` 读取五个 API 文件,用正则逐个断言 URL、HTTP 方法和关键参数名称;同时为工具写可执行单元测试:
|
||||
|
||||
```js
|
||||
test('扫码值只去除首尾空白并拒绝空值', async () => {
|
||||
const { normalizeScanValue } = await import('../src/utils/palletScan.js')
|
||||
assert.equal(normalizeScanValue(' QR-001\n'), 'QR-001')
|
||||
assert.throws(() => normalizeScanValue(' \n\t '), /二维码不能为空/)
|
||||
})
|
||||
|
||||
test('幂等键使用 randomUUID 或安全随机字节,不使用时间和伪随机数', async () => {
|
||||
const { createIdempotencyKey } = await import('../src/utils/palletScan.js')
|
||||
const expected = '12345678-1234-4123-8123-123456789abc'
|
||||
assert.equal(createIdempotencyKey({ randomUUID: () => expected }), expected)
|
||||
assert.equal(
|
||||
createIdempotencyKey({
|
||||
getRandomValues(bytes) {
|
||||
bytes.fill(0)
|
||||
return bytes
|
||||
}
|
||||
}),
|
||||
'00000000-0000-4000-8000-000000000000'
|
||||
)
|
||||
})
|
||||
|
||||
test('同一值只在重复时间窗口内被识别', async () => {
|
||||
const { isDuplicateScan } = await import('../src/utils/palletScan.js')
|
||||
assert.equal(isDuplicateScan('QR-001', 'QR-001', 1200, 1000, 500), true)
|
||||
assert.equal(isDuplicateScan('QR-001', 'QR-002', 1200, 1000, 500), false)
|
||||
assert.equal(isDuplicateScan('QR-001', 'QR-001', 1801, 1000, 500), false)
|
||||
})
|
||||
```
|
||||
|
||||
API 契约测试至少覆盖以下请求:入库和发货的列表、选项、扫码、详情、草稿增改删、确认;装填和使用的列表、选项、扫码、详情、提交;退回的列表、选项、扫码、申请、详情、确认。测试还要断言确认/写请求的函数名和 `data`/`params` 位置,防止把 query 字段误放入 body。
|
||||
|
||||
- [ ] **Step 2: 运行失败测试确认缺口**
|
||||
|
||||
工作目录:`shierfeng-fastapi-app`
|
||||
|
||||
```bash
|
||||
node --test tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
预期:因五个 API 模块、扫码工具和测试脚本尚未存在而失败;此时不修改后端或依赖。
|
||||
|
||||
- [ ] **Step 3: 实现最小 API 封装和测试脚本**
|
||||
|
||||
`package.json` 只增加以下脚本,不增加依赖:
|
||||
|
||||
```json
|
||||
"test:business": "node --test tests/business-contract.test.mjs"
|
||||
```
|
||||
|
||||
每个 API 文件只做路径和参数映射。例如入库模块必须保持如下接口形状:
|
||||
|
||||
```js
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function listInbound(query) {
|
||||
return request({ url: '/business/inbound/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function scanInboundPallet(qrToken, query) {
|
||||
return request({ url: '/business/inbound/scan/' + qrToken, method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function addInbound(data) {
|
||||
return request({ url: '/business/inbound', method: 'post', data })
|
||||
}
|
||||
|
||||
export function confirmInbound(inboundId, data) {
|
||||
return request({
|
||||
url: '/business/inbound/' + inboundId + '/confirm',
|
||||
method: 'put',
|
||||
data,
|
||||
headers: { repeatSubmit: false }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
其他函数按接口表逐一实现,不增加前端字段别名。`createIdempotencyKey` 优先使用 `crypto.randomUUID()`,否则用 `crypto.getRandomValues(new Uint8Array(16))` 按 UUID v4 设置版本和变体位;缺少安全随机源时抛出明确错误,不使用 `Date.now()`、`Math.random()` 或账号信息。
|
||||
|
||||
- [ ] **Step 4: 运行测试确认 API 与工具契约通过**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
```
|
||||
|
||||
预期:本任务新增的 API 路径、HTTP 方法、参数位置、扫码规范化、重复窗口和 UUID 测试通过;页面相关测试在后续任务加入。
|
||||
|
||||
- [ ] **Step 5: 只提交本任务文件**
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
shierfeng-fastapi-app/package.json \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs \
|
||||
shierfeng-fastapi-app/src/api/business/inbound.js \
|
||||
shierfeng-fastapi-app/src/api/business/filling.js \
|
||||
shierfeng-fastapi-app/src/api/business/shipment.js \
|
||||
shierfeng-fastapi-app/src/api/business/usage.js \
|
||||
shierfeng-fastapi-app/src/api/business/return.js \
|
||||
shierfeng-fastapi-app/src/utils/palletScan.js
|
||||
git commit --only -m '新增:PDA 业务 API 与扫码幂等工具' -- \
|
||||
shierfeng-fastapi-app/package.json \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs \
|
||||
shierfeng-fastapi-app/src/api/business/inbound.js \
|
||||
shierfeng-fastapi-app/src/api/business/filling.js \
|
||||
shierfeng-fastapi-app/src/api/business/shipment.js \
|
||||
shierfeng-fastapi-app/src/api/business/usage.js \
|
||||
shierfeng-fastapi-app/src/api/business/return.js \
|
||||
shierfeng-fastapi-app/src/utils/palletScan.js
|
||||
```
|
||||
|
||||
## Task 2: 先测试再实现 HID 扫码输入组件
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `shierfeng-fastapi-app/src/components/business/PalletScanInput.vue`
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes `normalizeScanValue` and `isDuplicateScan` from `@/utils/palletScan`.
|
||||
- Props: `disabled:Boolean=false`、`loading:Boolean=false`、`duplicateWindowMs:Number=500`。
|
||||
- Emits: `scan(normalizedValue)`、`invalid(message)`、`duplicate(normalizedValue)`。
|
||||
- Exposes no business state; parent controls request lock through `loading` and owns accepted pallet rows.
|
||||
|
||||
- [ ] **Step 1: 写组件静态契约测试并确认 RED**
|
||||
|
||||
在 `business-contract.test.mjs` 读取组件源码,断言存在 `@confirm="handleSubmit"`、`normalizeScanValue`、`isDuplicateScan`、三个 emit 名称、`:disabled="disabled || loading"` 或等价锁定逻辑,并断言没有 `uni.scanCode`、摄像头 API 或状态机字段判断。执行 `pnpm test:business`,预期组件测试失败。
|
||||
|
||||
- [ ] **Step 2: 实现组件输入状态机**
|
||||
|
||||
组件使用 uni-app 原生 `<input>`,同时绑定 `@confirm`(HID 回车和手工确认)与一个可点击的“提交扫描”按钮;输入框在 `disabled` 或 `loading` 时只读/禁用。提交逻辑必须保持以下顺序:
|
||||
|
||||
```js
|
||||
function handleSubmit() {
|
||||
if (props.disabled || props.loading) return
|
||||
let value
|
||||
try {
|
||||
value = normalizeScanValue(scanValue.value)
|
||||
} catch (error) {
|
||||
errorMessage.value = error.message
|
||||
emit('invalid', error.message)
|
||||
focusInput(true)
|
||||
return
|
||||
}
|
||||
const scannedAt = Date.now()
|
||||
if (isDuplicateScan(value, lastScanValue, scannedAt, lastScanAt, props.duplicateWindowMs)) {
|
||||
scanValue.value = ''
|
||||
emit('duplicate', value)
|
||||
focusInput()
|
||||
return
|
||||
}
|
||||
lastScanValue = value
|
||||
lastScanAt = scannedAt
|
||||
scanValue.value = ''
|
||||
emit('scan', value)
|
||||
focusInput()
|
||||
}
|
||||
```
|
||||
|
||||
`Date.now()`只用于比较短时重复输入,不得用于生成业务幂等键。组件自动聚焦、扫描完成后清空输入并重新聚焦;页面切换、请求进行中和权限不足时由父页面传入 `disabled/loading`。
|
||||
|
||||
- [ ] **Step 3: 运行组件契约和工具测试确认 GREEN**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
```
|
||||
|
||||
预期:组件的 HID 回车、手工提交、空值、重复值和请求锁静态契约通过。
|
||||
|
||||
- [ ] **Step 4: 提交组件**
|
||||
|
||||
```bash
|
||||
git add -- shierfeng-fastapi-app/src/components/business/PalletScanInput.vue shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
git commit --only -m '新增:PDA HID 扫码输入组件' -- \
|
||||
shierfeng-fastapi-app/src/components/business/PalletScanInput.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
## Task 3: 注册路由、移除密码预填并增加权限入口
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `shierfeng-fastapi-app/src/pages.json`
|
||||
- Modify: `shierfeng-fastapi-app/src/pages/login.vue`
|
||||
- Modify: `shierfeng-fastapi-app/src/pages/work/index.vue`
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Produces routes `pages/business/inbound/index`、`pages/business/filling/index`、`pages/business/shipment/index`、`pages/business/usage/index`、`pages/business/return/index`。
|
||||
- Uses existing `$auth.hasPermiOr`/`$auth.hasPermiAnd` and `$tab.navigateTo`; does not change Pinia or permission storage.
|
||||
|
||||
- [ ] **Step 1: 写路由、权限和凭据安全测试并确认 RED**
|
||||
|
||||
测试必须断言五个页面路径和标题存在,工作台包含五类业务权限字符串,且 `src/pages/login.vue` 不再包含旧默认密码或其他密码字面量。测试也要断言入口跳转路径与页面路径一致。
|
||||
|
||||
- [ ] **Step 2: 注册五个页面并实现入口过滤**
|
||||
|
||||
在 `pages.json` 按工作台之后加入五个页面,使用中文标题“仓库入库”“托盘装填”“发货出库”“现场使用”“退回仓库”。工作台增加“现场业务”区块,入口权限使用以下 OR 集合:
|
||||
|
||||
```js
|
||||
const businessEntries = [
|
||||
{ path: '/pages/business/inbound/index', permissions: ['business:inbound:list', 'business:inbound:query', 'business:inbound:add', 'business:inbound:edit', 'business:inbound:remove', 'business:inbound:confirm'] },
|
||||
{ path: '/pages/business/filling/index', permissions: ['business:filling:list', 'business:filling:query', 'business:filling:add'] },
|
||||
{ path: '/pages/business/shipment/index', permissions: ['business:shipment:list', 'business:shipment:query', 'business:shipment:add', 'business:shipment:edit', 'business:shipment:remove', 'business:shipment:confirm'] },
|
||||
{ path: '/pages/business/usage/index', permissions: ['business:usage:list', 'business:usage:query', 'business:usage:add'] },
|
||||
{ path: '/pages/business/return/index', permissions: ['business:return:list', 'business:return:query', 'business:return:request', 'business:return:confirm'] }
|
||||
]
|
||||
```
|
||||
|
||||
模板只渲染 `proxy.$auth.hasPermiOr(entry.permissions)` 为真的入口;点击前再次检查权限并调用 `$tab.navigateTo(entry.path)`。页面没有任何授权时不显示业务区块。保留现有非本任务系统入口,不扩展其他模块。
|
||||
|
||||
- [ ] **Step 3: 清除登录页密码默认值**
|
||||
|
||||
将 `loginForm` 的 `username` 和 `password` 初始值改为空字符串,验证码和登录 API 流程保持不变;开发联调账号只在人工输入时使用,不写入源码、测试或日志。
|
||||
|
||||
- [ ] **Step 4: 运行测试确认 GREEN**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
```
|
||||
|
||||
预期:五个路由、权限入口、登录安全静态契约通过。
|
||||
|
||||
- [ ] **Step 5: 提交页面壳层改动**
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
shierfeng-fastapi-app/src/pages.json \
|
||||
shierfeng-fastapi-app/src/pages/login.vue \
|
||||
shierfeng-fastapi-app/src/pages/work/index.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
git commit --only -m '新增:PDA 业务页面路由与权限入口' -- \
|
||||
shierfeng-fastapi-app/src/pages.json \
|
||||
shierfeng-fastapi-app/src/pages/login.vue \
|
||||
shierfeng-fastapi-app/src/pages/work/index.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
## Task 4: 实现首次入库 PDA 页面
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `shierfeng-fastapi-app/src/pages/business/inbound/index.vue`
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes all exports from `src/api/business/inbound.js`, `PalletScanInput`, `createIdempotencyKey` and `$auth`.
|
||||
- Produces page methods `handleInboundScan`, `submitInboundDraft`, `saveInboundUpdate`, `confirmInboundDraft`, `removeScannedPallet`, `resetInboundOperation`。
|
||||
- Page state stores only scanned display rows, draft form values, request/loading/error state and confirm idempotency key; it never stores a client-calculated inventory state。
|
||||
|
||||
- [ ] **Step 1: 写入库页面契约测试并确认 RED**
|
||||
|
||||
测试读取页面源码并断言:
|
||||
|
||||
1. 使用 `PalletScanInput`,扫描调用 `scanInboundPallet(qrToken, { warehouseId })`。
|
||||
2. 只接受 `flowState === 'UNSTOCKED'`,按 `palletId` 去重。
|
||||
3. 草稿 payload 只含 `{ warehouseId, palletIds }`,编辑 payload 只含 `{ inboundId, warehouseId, palletIds }`。
|
||||
4. 确认调用 `confirmInbound(inboundId, { idempotencyKey })`,失败分支不清空当前键。
|
||||
5. 编辑/删除/确认按钮分别包含 `business:inbound:edit/remove/confirm`,入口操作需要同时具备 `query` 与对应写权限。
|
||||
|
||||
- [ ] **Step 2: 实现仓库选项、草稿扫码和编辑状态**
|
||||
|
||||
页面加载授权仓库选项和第一页列表;新增时选择仓库后允许连续扫描。每次扫码携带当前仓库 ID,接受服务端返回的 `palletId/palletCode/flowState`,重复托盘只提示不追加。切换仓库清空当前扫描集合并使旧扫码响应失效。
|
||||
|
||||
编辑时先调用 `getInbound(inboundId)`,只有详情仍为 `DRAFT` 才填充仓库和明细;详情未返回前禁止扫码、保存和删除。列表刷新/页面 `onShow` 以服务端 `rows` 为准,不从本地草稿推导状态。
|
||||
|
||||
- [ ] **Step 3: 实现草稿写入和确认重试**
|
||||
|
||||
```js
|
||||
function submitInboundDraft() {
|
||||
return addInbound({
|
||||
warehouseId: form.value.warehouseId,
|
||||
palletIds: scannedPallets.value.map((item) => item.palletId)
|
||||
})
|
||||
}
|
||||
|
||||
function confirmInboundDraft(row) {
|
||||
const idempotencyKey = ensureConfirmIdempotencyKey(row.inboundId)
|
||||
confirmLoading.value = true
|
||||
confirmInbound(row.inboundId, { idempotencyKey })
|
||||
.then(() => {
|
||||
clearConfirmIdempotency()
|
||||
loadInboundList()
|
||||
})
|
||||
.catch(() => {
|
||||
confirmError.value = '入库确认失败;再次确认将复用本次幂等键'
|
||||
})
|
||||
.finally(() => {
|
||||
confirmLoading.value = false
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
后端新增草稿响应不保证返回单据 ID,因此保存后只关闭编辑区并重新读取列表,由用户从服务端返回的 `DRAFT` 行选择确认,不按时间或本地数组猜测刚创建的单据。确认成功后清除键;网络超时、401 以外的可重试失败保留键,401 交由现有 `request` 会话处理。
|
||||
|
||||
- [ ] **Step 4: 运行入库契约和构建前静态检查**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
```
|
||||
|
||||
预期:入库页面的字段、状态、权限、幂等键和服务端刷新契约通过。
|
||||
|
||||
- [ ] **Step 5: 提交入库页面**
|
||||
|
||||
```bash
|
||||
git add -- shierfeng-fastapi-app/src/pages/business/inbound/index.vue shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
git commit --only -m '新增:PDA 首次入库操作页' -- \
|
||||
shierfeng-fastapi-app/src/pages/business/inbound/index.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
## Task 5: 实现整托发货 PDA 页面
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `shierfeng-fastapi-app/src/pages/business/shipment/index.vue`
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes `src/api/business/shipment.js`、`PalletScanInput` 和 `createIdempotencyKey`。
|
||||
- Produces `handleShipmentScan`, `submitShipmentDraft`, `saveShipmentUpdate`, `confirmShipmentDraft`, `removeScannedPallet`。
|
||||
- Scanned rows expose `palletCode`, `strainName/strainCode`, `batchNo`, `remainingPackages` as read-only display values。
|
||||
|
||||
- [ ] **Step 1: 写发货页面失败契约**
|
||||
|
||||
断言页面加载仓库和现场选项,扫描调用 `scanShipmentPallet(qrToken, { sourceWarehouseId, targetSiteId })`,只接受 `flowState === 'FILLED_IN_WAREHOUSE' && remainingPackages > 0`,草稿/确认字段与接口表一致,并保留确认失败时的幂等键。
|
||||
|
||||
- [ ] **Step 2: 实现来源仓库、目标现场和集合扫码**
|
||||
|
||||
来源仓库或目标现场改变时清空托盘集合并使旧请求失效。扫描期间锁定输入和提交按钮;同一 `palletId` 不重复追加。列表、详情和选项失败显示页面内可重试错误,不吞掉后端业务消息。
|
||||
|
||||
- [ ] **Step 3: 实现草稿和确认**
|
||||
|
||||
```js
|
||||
function submitShipmentDraft() {
|
||||
return addShipment({
|
||||
sourceWarehouseId: form.value.sourceWarehouseId,
|
||||
targetSiteId: form.value.targetSiteId,
|
||||
palletIds: scannedPallets.value.map((item) => item.palletId)
|
||||
})
|
||||
}
|
||||
|
||||
function confirmShipmentDraft(row) {
|
||||
const idempotencyKey = ensureConfirmIdempotencyKey(row.shipmentId)
|
||||
return confirmShipment(row.shipmentId, { idempotencyKey })
|
||||
}
|
||||
```
|
||||
|
||||
保存后重新读取列表;确认前要求 `DRAFT`,成功后重新加载列表和页面状态。错误重试只复用同一确认键,改变单据或托盘集合时清除并重新生成键;不在前端扣减 `remainingPackages`。
|
||||
|
||||
- [ ] **Step 4: 运行测试并提交**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
git add -- shierfeng-fastapi-app/src/pages/business/shipment/index.vue shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
git commit --only -m '新增:PDA 整托发货操作页' -- \
|
||||
shierfeng-fastapi-app/src/pages/business/shipment/index.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
预期:测试通过后才执行提交;提交只包含发货页面和契约测试增量。
|
||||
|
||||
## Task 6: 实现单托盘装填 PDA 页面
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `shierfeng-fastapi-app/src/pages/business/filling/index.vue`
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes `listFilling`、`getFillingStrainOptions`、`scanFillingPallet`、`addFilling` 和 `PalletScanInput`。
|
||||
- Produces form fields `qrToken`, `warehouseId`, `strainId`, `batchNo`, `packageSpec`, `initialPackages` and one operation-scoped `idempotencyKey`。
|
||||
|
||||
- [ ] **Step 1: 写装填页面失败契约**
|
||||
|
||||
断言页面只接受 `flowState === 'EMPTY_IN_WAREHOUSE'`,扫码成功后展示仓库和容量,提交 payload 包含且只包含后端 `FillingCreateModel` 字段,包数校验为严格正整数,成功/失败状态不伪造库存。
|
||||
|
||||
- [ ] **Step 2: 实现扫码和表单**
|
||||
|
||||
扫码后保存服务端返回的 `qrToken`、`warehouseId`、`palletCode`、`capacityPackages`,加载菌种选项。菌种、批次、规格和包数在请求期间锁定;包数的前端校验只做正整数和非空检查,容量和托盘状态由后端最终判定。
|
||||
|
||||
- [ ] **Step 3: 实现幂等提交和成功刷新**
|
||||
|
||||
```js
|
||||
function submitFilling() {
|
||||
const idempotencyKey = ensureFillingIdempotencyKey()
|
||||
return addFilling({
|
||||
qrToken: form.value.qrToken,
|
||||
warehouseId: form.value.warehouseId,
|
||||
strainId: form.value.strainId,
|
||||
batchNo: form.value.batchNo.trim(),
|
||||
packageSpec: form.value.packageSpec.trim(),
|
||||
initialPackages: form.value.initialPackages,
|
||||
idempotencyKey
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
提交成功后清空表单并刷新装填列表,再以扫码查询或列表结果显示服务端状态;刷新失败不能覆盖已经成功的写入提示。提交失败保留键;修改托盘、菌种、批次、规格或包数后清除键,下一次输入生成新键。
|
||||
|
||||
- [ ] **Step 4: 运行测试并提交**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
git add -- shierfeng-fastapi-app/src/pages/business/filling/index.vue shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
git commit --only -m '新增:PDA 托盘装填操作页' -- \
|
||||
shierfeng-fastapi-app/src/pages/business/filling/index.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
## Task 7: 实现客户现场使用 PDA 页面
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `shierfeng-fastapi-app/src/pages/business/usage/index.vue`
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes `listUsage`、`getUsageLineOptions(siteId)`、`scanUsagePallet`、`addUsage` 和 `PalletScanInput`。
|
||||
- Produces `handleUsageScan`, `loadLineOptions`, `submitUsage`, `resetUsageOperation`。
|
||||
- Uses `UsageSuccessResponseModel` fields `usageId`, `remainingPackages`, `flowState` for result display only。
|
||||
|
||||
- [ ] **Step 1: 写现场使用失败契约**
|
||||
|
||||
断言页面只接受 `AT_SITE_UNUSED` 或 `AT_SITE_PARTIAL` 且 `remainingPackages > 0`;没有 `currentLineId` 时按扫码响应的 `siteId` 获取产线,有绑定时锁定该产线;提交使用 `qrToken,lineId,usedPackages,idempotencyKey`,不在本地推算新状态。
|
||||
|
||||
- [ ] **Step 2: 实现扫码、产线锁定和正整数校验**
|
||||
|
||||
每次新扫码先使旧产线请求失效、清空旧结果和幂等键。扫码成功后保存托盘、菌种、批次、余量和现场信息;首次使用显示可选产线,已有 `currentLineId` 时只能使用该产线。使用包数必须是严格正整数,超过当前展示余量时给出提示,但即使通过前端检查也仍由服务端校验。
|
||||
|
||||
- [ ] **Step 3: 实现使用提交和结果刷新**
|
||||
|
||||
```js
|
||||
function submitUsage() {
|
||||
const idempotencyKey = ensureUsageIdempotencyKey()
|
||||
return addUsage({
|
||||
qrToken: form.value.qrToken,
|
||||
lineId: form.value.lineId,
|
||||
usedPackages: form.value.usedPackages,
|
||||
idempotencyKey
|
||||
}).then((response) => {
|
||||
usageResult.value = response.data
|
||||
clearUsageIdempotencyKey()
|
||||
return loadUsageList()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
网络失败时保留键并显示“再次提交将复用本次幂等键”;成功只采用 `response.data.remainingPackages` 和 `response.data.flowState`,然后清理表单或允许重新扫码。列表和扫码响应晚于新会话时必须被请求代数丢弃。
|
||||
|
||||
- [ ] **Step 4: 运行测试并提交**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
git add -- shierfeng-fastapi-app/src/pages/business/usage/index.vue shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
git commit --only -m '新增:PDA 客户现场使用操作页' -- \
|
||||
shierfeng-fastapi-app/src/pages/business/usage/index.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
## Task 8: 实现托盘退回申请与仓库确认
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `shierfeng-fastapi-app/src/pages/business/return/index.vue`
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes all exports from `src/api/business/return.js` and two independent `PalletScanInput` instances。
|
||||
- Request side state: `sourceSiteId`, `targetWarehouseId`, `requestScannedPallets`, `requestIdempotencyKey`。
|
||||
- Confirm side state: `returnId`, `expectedPalletIds`, `confirmScannedPallets`, `confirmIdempotencyKey`。
|
||||
- Produces `handleRequestScan`, `submitReturnRequest`, `loadReturnDetail`, `handleConfirmScan`, `submitReturnConfirm`, `sameIdSet`。
|
||||
|
||||
- [ ] **Step 1: 写退回两阶段失败契约**
|
||||
|
||||
断言测试覆盖:
|
||||
|
||||
1. 申请扫码使用 `{ mode: 'request' }`,首个托盘确定 `sourceSiteId`,后续托盘必须属于同一现场。
|
||||
2. 申请 payload 为 `sourceSiteId,targetWarehouseId,palletIds,idempotencyKey`,申请键和确认键互不复用。
|
||||
3. 确认先读取详情,状态必须为 `REQUESTED`,扫描使用 `{ mode: 'confirm', returnId }`,扫码集合必须与详情托盘 ID 集合完全相同。
|
||||
4. 确认写请求前再次 `getReturn(returnId)`,比较最新状态和 ID 集合后才调用 `confirmReturn`。
|
||||
5. 请求或确认扫码/提交的旧响应不能覆盖新选择;失败不清除对应幂等键。
|
||||
|
||||
- [ ] **Step 2: 实现申请区**
|
||||
|
||||
加载可访问目标仓库和退回列表。申请扫码成功后保存 `palletId,palletCode,siteId,remainingPackages`,同一托盘不重复,目标仓库或会话变化时清空集合和申请键。`requestReturn` 成功后清除申请键、清空申请区并刷新列表;不根据响应消息猜测新 `returnId`,确认入口从服务端返回的 `REQUESTED` 列表选择单据。
|
||||
|
||||
- [ ] **Step 3: 实现确认区的详情快照与整集合扫码**
|
||||
|
||||
选择退回单后调用 `getReturn`,保存详情中的 `items[].palletId` 作为预期集合。确认扫码必须命中预期集合且不能重复;切换退回单使确认扫码代数、详情代数和确认键同时失效。实现集合比较时使用去重后的 `Set`,长度和成员都必须一致:
|
||||
|
||||
```js
|
||||
function sameIdSet(left, right) {
|
||||
if (left.length !== right.length) return false
|
||||
const leftSet = new Set(left)
|
||||
const rightSet = new Set(right)
|
||||
return leftSet.size === left.length &&
|
||||
rightSet.size === right.length &&
|
||||
right.every((id) => leftSet.has(id))
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 实现确认前复核和幂等提交**
|
||||
|
||||
```js
|
||||
async function submitReturnConfirm() {
|
||||
const returnId = confirmForm.value.returnId
|
||||
const scannedPalletIds = confirmScannedPallets.value.map((item) => item.palletId)
|
||||
if (!returnId || !sameIdSet(expectedPalletIds.value, scannedPalletIds)) {
|
||||
confirmError.value = '必须逐个扫描并完整匹配退回单全部托盘'
|
||||
return
|
||||
}
|
||||
const latest = (await getReturn(returnId)).data
|
||||
if (latest.status !== 'REQUESTED' ||
|
||||
!sameIdSet(latest.items.map((item) => item.palletId), scannedPalletIds)) {
|
||||
confirmError.value = '退回单状态或托盘明细已变化,请重新选择并扫码'
|
||||
return
|
||||
}
|
||||
const idempotencyKey = ensureConfirmIdempotencyKey(returnId)
|
||||
await confirmReturn(returnId, { scannedPalletIds, idempotencyKey })
|
||||
}
|
||||
```
|
||||
|
||||
确认成功后清除确认键、刷新退回列表和详情;网络失败保留键。申请确认两个阶段的输入锁、错误状态和重试键必须分开,不能用一个全局 `loading` 或一个幂等键混用。
|
||||
|
||||
- [ ] **Step 5: 运行测试并提交**
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
git add -- shierfeng-fastapi-app/src/pages/business/return/index.vue shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
git commit --only -m '新增:PDA 托盘退回两阶段操作页' -- \
|
||||
shierfeng-fastapi-app/src/pages/business/return/index.vue \
|
||||
shierfeng-fastapi-app/tests/business-contract.test.mjs
|
||||
```
|
||||
|
||||
## Task 9: 完成全量契约验证、H5 构建和交付检查
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `shierfeng-fastapi-app/tests/business-contract.test.mjs`(仅在发现测试覆盖缺口时补充)
|
||||
- Preserve: 所有后端、Web、环境、日志、缓存和用户已有 APP 文件
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes all five PDA pages, five API modules, shared scanner and existing backend contract。
|
||||
- Produces a verified H5/development build;does not produce an APK。
|
||||
|
||||
- [ ] **Step 1: 完成静态契约覆盖清单**
|
||||
|
||||
测试必须逐项覆盖:
|
||||
|
||||
- 五个 API 文件的路径、方法、参数位置和导出函数。
|
||||
- 五个页面的路由存在、页面调用对应 API、camelCase payload 字段正确。
|
||||
- 五类状态条件:`UNSTOCKED`、`EMPTY_IN_WAREHOUSE`、`FILLED_IN_WAREHOUSE`、`AT_SITE_UNUSED/AT_SITE_PARTIAL`、退回 `REQUESTED`。
|
||||
- HID 回车、空值、重复扫码、扫描请求锁和手工输入。
|
||||
- 入库/发货确认、装填、使用、退回申请/确认的幂等键生成、保留和成功清理时机。
|
||||
- 页面权限标识和登录页无密码预填。
|
||||
- 不存在摄像头扫码调用、APK 构建脚本调用、管理员密码或 Token 字面量。
|
||||
|
||||
- [ ] **Step 2: 运行 PDA 契约测试**
|
||||
|
||||
工作目录:`shierfeng-fastapi-app`
|
||||
|
||||
```bash
|
||||
pnpm test:business
|
||||
```
|
||||
|
||||
预期:所有 Node 20 测试通过且零失败。
|
||||
|
||||
- [ ] **Step 3: 运行 H5 构建,不运行 APK 构建**
|
||||
|
||||
```bash
|
||||
pnpm run build:h5
|
||||
```
|
||||
|
||||
预期:uni-app H5 构建成功。禁止运行 `pnpm run build:app`、`pnpm run dev:app`、APK 签名、安装或发布命令;构建产物不加入提交。
|
||||
|
||||
- [ ] **Step 4: 运行已有后端业务回归**
|
||||
|
||||
工作目录:`shierfeng-fastapi-backend`,使用当前已配置 Python 环境,不安装依赖:
|
||||
|
||||
```bash
|
||||
PYTHONDONTWRITEBYTECODE=1 python -m pytest tests/module_business -q
|
||||
ruff format --check module_business tests/module_business
|
||||
ruff check module_business tests/module_business
|
||||
```
|
||||
|
||||
预期:已有后端业务测试、格式检查和静态检查通过;若本地 Python 环境无法执行,记录实际错误,不伪造通过结果。
|
||||
|
||||
- [ ] **Step 5: 做最终 diff 与敏感信息检查**
|
||||
|
||||
仓库根目录执行:
|
||||
|
||||
```bash
|
||||
git diff --check HEAD~9..HEAD
|
||||
rg -n --fixed-strings '旧默认密码' shierfeng-fastapi-app/src shierfeng-fastapi-app/tests
|
||||
rg -n 'Bearer[[:space:]]+[A-Za-z0-9._-]+' shierfeng-fastapi-app/src shierfeng-fastapi-app/tests
|
||||
git status --short
|
||||
```
|
||||
|
||||
敏感信息检查应无命中;`git status` 中只允许出现本计划任务的 PDA 文件和用户原有改动,不能出现由构建生成且准备提交的 APK、环境文件、日志或缓存。若提交数量因任务合并而不同,将 `HEAD~9..HEAD` 改为本次 PDA 首个提交到当前 `HEAD` 的实际范围,不扩大检查目标。
|
||||
|
||||
- [ ] **Step 6: 按人工验收清单交付**
|
||||
|
||||
在受控本地环境手工验证:管理员和受限普通用户登录;未授权入口不显示且直接接口返回 403;HID 扫码回车、重复扫码、空值和网络重试;库存余量、托盘状态、产线绑定、退回冻结与事件追溯均来自服务端;页面刷新和断网重试不重复扣减库存。管理员密码只在人工输入,不记录到终端输出、源码或日志。
|
||||
|
||||
## 计划自检
|
||||
|
||||
- 设计文档的范围、五类流程、认证安全、扫码输入、状态/幂等、验证、部署边界分别由 Tasks 1–9 覆盖。
|
||||
- 所有跨任务接口在“已核实的后端接口与字段”和各任务的 `Interfaces` 中定义,页面调用名称与 API 导出名称保持一致。
|
||||
- 没有新增依赖、后端契约改动、摄像头 SDK、APK 构建或凭据写入步骤。
|
||||
- 实现前先写失败契约,随后运行最小测试,再执行 H5 和后端回归;每个任务都以显式路径提交。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
||||
# MySQL Initialization and Docker Compose Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Keep the MySQL fresh-install script aligned with the current business model and provide a safe, persistent Compose deployment for the frontend, backend, MySQL, and Redis.
|
||||
|
||||
**Architecture:** The existing MySQL initializer remains the single bootstrap source for a new empty database and is mounted into MySQL's first-start initialization directory. Existing database volumes are never migrated automatically; operators continue to use the reviewed MySQL patches separately. Compose keeps MySQL and Redis on an internal network, persists their data in named volumes, and reads credentials from an ignored local `.env` file created from a checked-in example.
|
||||
|
||||
**Tech Stack:** MySQL 8.0, Redis 7, existing FastAPI and Vue/Nginx Dockerfiles, Docker Compose v2, Python `unittest` schema contracts.
|
||||
|
||||
**Spec:** User request; schema/seed contract in `shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py`; operational migration guidance in `docs/test-data-batch-operations.md` and `docs/pallet-workflow-deployment.md`.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- MySQL remains the only database engine; do not add PostgreSQL configuration or SQL files.
|
||||
- Do not execute SQL, apply patches, start containers, or remove/replace any database volume as part of this change.
|
||||
- The full initializer contains `DROP TABLE`; mount it only as a first-start script for a fresh MySQL data directory.
|
||||
- Keep existing seed records and schema unchanged when the current contract tests confirm they are synchronized.
|
||||
- Do not place real credentials in Compose or the checked-in example environment file.
|
||||
- Preserve unrelated staged, unstaged, and untracked work already in the repository.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Mark the MySQL initializer's safe-use boundary
|
||||
|
||||
**Files:**
|
||||
- Modify: `shierfeng-fastapi-backend/sql/shierfeng-fastapi.sql`
|
||||
- Test: `shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: current SQLAlchemy ORM table metadata and the existing MySQL SQL contract tests.
|
||||
- Produces: the same schema and seed statements, with a clear warning that this destructive bootstrap is only for an empty/new database.
|
||||
|
||||
- [x] Add a Chinese header warning that the script drops/recreates tables and is for a fresh empty database only; direct existing-database users to reviewed files under `sql/patches/`.
|
||||
- [x] Run the existing SQL contract suite and confirm all table, field, constraint, menu, and test-batch contracts still pass.
|
||||
- [x] Review the diff to confirm no seed rows or DDL changed beyond the warning.
|
||||
|
||||
### Task 2: Add a persistent full-stack Docker Compose deployment
|
||||
|
||||
**Files:**
|
||||
- Create: `docker-compose.yml`
|
||||
- Create: `.env.docker.example`
|
||||
- Modify: `.gitignore`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `Dockerfile.my`, the frontend `Dockerfile`, `nginx.dockermy.conf`, `.env.dockermy`, and the MySQL initializer.
|
||||
- Produces: Compose services `shierfeng-mysql`, `shierfeng-redis`, `shierfeng-backend-my`, and `shierfeng-frontend`; named volumes `mysql-data` and `redis-data`; required secrets supplied by root `.env`.
|
||||
|
||||
- [x] Define MySQL 8.0 with the fresh-install SQL mounted read-only at `/docker-entrypoint-initdb.d/01-schema-and-seed.sql`, an app-scoped database user, a health check, and a named data volume.
|
||||
- [x] Define Redis 7 with append-only persistence, a health check, and a named data volume; do not publish Redis to the host.
|
||||
- [x] Build the backend and frontend from the existing Dockerfiles. Configure backend DB/Redis service hosts and credentials through Compose environment overrides; make backend wait for healthy dependencies.
|
||||
- [x] Route browser API requests through the existing Nginx config. Publish the frontend on configurable host port 8080 by default and MySQL only on loopback port 13307 by default.
|
||||
- [x] Create `.env.docker.example` with placeholders for MySQL root password, app DB username/password, and JWT secret; add only root `/.env` to `.gitignore`.
|
||||
- [x] Run `docker compose config --quiet` with test-only environment values and confirm configuration resolves without starting services.
|
||||
|
||||
### Task 3: Document first-run and existing-database workflows
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: root Compose file and `.env.docker.example`.
|
||||
- Produces: concise setup commands and explicit distinction between fresh database initialization and existing-database patches.
|
||||
|
||||
- [x] Replace the root deployment instructions that point to the test-only Compose file with instructions to copy `.env.docker.example` to `.env`, set strong local credentials, validate with `docker compose config`, and start with `docker compose up -d --build`.
|
||||
- [x] Document the default frontend/MySQL addresses and state that MySQL initialization scripts run only when its data directory is empty; existing databases must use reviewed patches after backup.
|
||||
- [x] Do not include `docker compose down -v` or any other volume-deletion command in the documentation.
|
||||
- [x] Re-run the SQL contract suite and Compose config validation; do not run `docker compose up`.
|
||||
|
||||
## Verification Commands
|
||||
|
||||
```bash
|
||||
PYTHONDONTWRITEBYTECODE=1 /Users/jiaolongyan/miniconda3/envs/ShiErFeng/bin/python -m unittest tests.module_business.test_sql_contract -v
|
||||
MYSQL_ROOT_PASSWORD=compose-validation DB_USERNAME=compose_validation DB_PASSWORD=compose-validation JWT_SECRET_KEY=compose-validation docker compose config --quiet
|
||||
```
|
||||
|
||||
Do not run the SQL initializer, migration patches, or `docker compose up` during implementation verification.
|
||||
@@ -0,0 +1,179 @@
|
||||
# PDA 托盘业务操作功能补全设计
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
当前仓库已经具备 Web 后台、FastAPI 业务接口、托盘库存状态机、数据库结构、权限模型和六类报表。`shierfeng-fastapi-app` 目前只有通用登录、首页、工作台和个人中心,没有托盘业务操作页面与对应 API 封装。
|
||||
|
||||
本次在不改变既有后端业务契约的前提下,补齐 uni-app PDA 操作端的现场业务功能。PDA 暂不生成 APK,先完成页面、接口对接、扫码交互、权限控制和可验证的开发运行版本。
|
||||
|
||||
## 2. 范围确认
|
||||
|
||||
### 2.1 本次包含
|
||||
|
||||
- 复用现有 `/login`、`/getInfo`、`/logout` 认证流程。
|
||||
- 在 `shierfeng-fastapi-app` 增加五类现场操作:
|
||||
- 仓库入库
|
||||
- 工厂装填
|
||||
- 发货出库
|
||||
- 客户现场使用
|
||||
- 退回仓库
|
||||
- 增加 PDA 端 API 模块、扫码输入组件、业务页面、权限显示和错误/重试处理。
|
||||
- 复用后端已有状态机、幂等键、数据范围、库存事务和托盘追溯,不在 PDA 前端自行计算最终库存或状态。
|
||||
- 适配 PDA 扫码头的 HID 键盘输入和回车提交,并保留手工输入作为故障兜底。
|
||||
- 增加 PDA 端静态契约测试和 H5/开发构建验证;不生成 APK。
|
||||
- 继续使用 Web 后台维护菌种、客户、现场、产线、仓库、托盘和六类报表。
|
||||
|
||||
### 2.2 本次不包含
|
||||
|
||||
- 不打包、签名、安装或发布 APK。
|
||||
- 不调用摄像头扫码,不新增原生扫码 SDK;当前以 HID 扫码枪输入为主。
|
||||
- 不把基础资料维护和六类报表复制到 PDA。
|
||||
- 不修改现有 FastAPI 业务接口的请求/响应契约,除非实现阶段发现有明确且可验证的 PDA 阻塞缺口。
|
||||
- 不修改 `shierfeng-fastapi-frontend` 的既有 Web 业务页面,除非共享契约缺陷直接阻塞 PDA。
|
||||
- 不修改 `.env.dev`、`.env.prod`、前端环境文件、用户已有 APP 改动或无关缓存。
|
||||
- 不执行生产部署、服务器配置、Redis 清理、数据库迁移或培训;这些属于后续运行交付,需要单独的环境授权和验收。
|
||||
- 不把管理员密码写入源码、设计文档、日志或测试固定文本。
|
||||
|
||||
## 3. 用户角色与权限边界
|
||||
|
||||
PDA 页面只展示当前用户拥有的权限,后端接口权限和仓库/现场数据范围仍是最终权威:
|
||||
|
||||
| 角色能力 | PDA 操作 |
|
||||
|---|---|
|
||||
| 仓库操作员 | 查看可访问仓库、创建/确认入库、扫描装填、创建/确认发货、确认退回 |
|
||||
| 客户现场操作员 | 扫描托盘、首次选择产线、录入使用包数、发起退回 |
|
||||
| 业务管理员/超级管理员 | 依据既有权限可执行全部授权操作 |
|
||||
|
||||
不新增固定角色,不自动给普通用户授权。仓库与客户现场的数据范围继续由现有用户-仓库、用户-现场关系决定。
|
||||
|
||||
## 4. 页面与 API 设计
|
||||
|
||||
### 4.1 PDA 页面
|
||||
|
||||
新增页面建议采用以下路径,沿用当前 uni-app 页面路由和 Tailwind 样式:
|
||||
|
||||
```text
|
||||
src/pages/business/inbound/index.vue
|
||||
src/pages/business/filling/index.vue
|
||||
src/pages/business/shipment/index.vue
|
||||
src/pages/business/usage/index.vue
|
||||
src/pages/business/return/index.vue
|
||||
```
|
||||
|
||||
工作台只增加实际权限允许的业务入口,不保留“建设中”假入口。页面加载失败、权限不足和接口异常必须给出可操作提示。
|
||||
|
||||
### 4.2 PDA API 封装
|
||||
|
||||
复用后台已有接口路径,在 `src/api/business/` 增加:
|
||||
|
||||
```text
|
||||
inbound.js
|
||||
filling.js
|
||||
shipment.js
|
||||
usage.js
|
||||
return.js
|
||||
```
|
||||
|
||||
每个模块覆盖对应的列表、详情、选项、扫码、创建/编辑/删除、确认或提交接口;请求字段使用后端既有 camelCase 契约,不在前端另建同义字段。
|
||||
|
||||
### 4.3 共享扫码组件
|
||||
|
||||
新增 PDA 专用扫码输入组件,职责仅限于输入采集和规范化:
|
||||
|
||||
- 自动聚焦可用输入框。
|
||||
- 兼容 HID 扫码枪逐字符输入后回车提交。
|
||||
- 去除首尾空白,拒绝空值。
|
||||
- 防止同一托盘在短时间内重复追加。
|
||||
- 扫码请求期间锁定相关输入,避免并发提交。
|
||||
- 支持“重新扫描”和手工输入兜底。
|
||||
- 不在组件内决定托盘状态、库存数量或权限结果。
|
||||
|
||||
## 5. 五类业务流程
|
||||
|
||||
### 5.1 入库
|
||||
|
||||
选择后端返回的可用仓库,创建草稿并连续扫描 `UNSTOCKED` 托盘;允许移除草稿明细,确认时复用业务幂等键。提交成功后以服务端结果刷新页面,不由 PDA 推算库存。
|
||||
|
||||
### 5.2 装填
|
||||
|
||||
扫描托盘并展示后端返回的仓库、状态和当前余量;选择菌种,填写批次、规格和正整数包数;提交后清理表单并重新获取托盘状态。非法状态、超容量、无仓库权限由后端拒绝并原样转为用户可读提示。
|
||||
|
||||
### 5.3 发货
|
||||
|
||||
选择来源仓库和目标客户现场,连续扫描多个可发货托盘;展示菌种、批次和剩余包数只读信息;确认前复核托盘集合,服务端负责整单锁定、库存扣减和事件追加。
|
||||
|
||||
### 5.4 客户现场使用
|
||||
|
||||
扫描托盘后展示菌种、批次、当前余量和允许操作;首次使用要求选择当前现场产线,后续使用沿用后端锁定产线;使用包数必须为正整数且不超过服务端返回余量。成功后只采用服务端返回的新余量和状态。
|
||||
|
||||
### 5.5 退回仓库
|
||||
|
||||
客户现场扫描待退回托盘,选择目标仓库并发起退回;仓库用户扫描并确认收货。申请和确认分别持有独立幂等键,页面分别保存两阶段状态,确认后以服务端返回的库存状态刷新。
|
||||
|
||||
## 6. 认证、密码与安全
|
||||
|
||||
- PDA 继续使用当前 JWT、传输加密和 `/getInfo` 权限信息。
|
||||
- 前端不硬编码管理员密码;给定账号只用于经授权的本地联调验证。
|
||||
- 当前登录页中的旧默认密码预填值需要改为不暴露密码的空表单或开发环境专用行为,不能把真实密码写入仓库。
|
||||
- 管理员密码变更属于账号凭据写操作,须先只读核对当前认证存储,再单独确认、备份并执行,日志不得输出密码、Token 或 Cookie。
|
||||
- PDA 页面隐藏不能替代后端权限校验;所有业务请求继续经过后端接口权限和数据范围检查。
|
||||
|
||||
## 7. 状态、幂等与错误处理
|
||||
|
||||
- PDA 只提交业务输入和幂等键,不提交目标库存、目标状态或版本号。
|
||||
- 网络超时、响应丢失或用户重试时复用同一个业务幂等键;同键不同请求内容显示冲突,不自动生成新业务操作。
|
||||
- 扫码重复、按钮重复点击和页面切换期间的旧响应由前端请求序列号/锁控制。
|
||||
- 401 继续走现有会话过期处理;403、业务状态冲突、库存不足和数据范围错误必须显示后端消息并允许安全重试。
|
||||
- 页面恢复或重新进入时,以详情/列表接口作为唯一权威,不能相信本地缓存的库存和状态。
|
||||
|
||||
## 8. 验证与交付
|
||||
|
||||
### 8.1 代码验证
|
||||
|
||||
- 为 PDA API 路径、字段映射、权限入口、扫码规范化、幂等键复用和五类页面交互增加 Node 20 契约测试。
|
||||
- 使用现有后端业务测试和 Ruff 验证共享接口没有回归。
|
||||
- 运行 uni-app 的 H5/开发构建或等价编译检查;不执行 `build:app`,不生成 APK。
|
||||
- 对当前工作区做精确 diff 检查,保留用户已有 APP、环境、日志和缓存改动。
|
||||
|
||||
### 8.2 人工验收清单
|
||||
|
||||
1. 使用管理员和受限普通用户分别登录。
|
||||
2. 验证未授权菜单不显示、直接接口请求返回 403。
|
||||
3. 使用 HID 扫码枪验证五类业务的扫码、回车、重复扫码和错误重试。
|
||||
4. 验证库存、剩余包数、产线绑定、退回冻结和事件追溯均以后端结果为准。
|
||||
5. 验证页面刷新/断网重试不重复扣减库存。
|
||||
|
||||
真实服务器部署、APK 安装、客户培训和跨设备实测在本次代码补全后仍需单独安排,不能用本地构建结果替代。
|
||||
|
||||
## 9. 当前基线与风险
|
||||
|
||||
- Web/FastAPI 核心业务已存在并有业务测试;本次重点是 uni-app PDA 缺失能力。
|
||||
- 当前 PDA 工程已包含通用登录、首页、工作台、个人中心,以及入库、装填、发货、现场使用、退回五类业务页面和对应 API 模块。
|
||||
- 后端接口、Web 管理页面和数据库 SQL 已存在;PDA 的实际设备联调、服务器访问地址、数据库迁移和真实角色授权仍需运行环境配合。
|
||||
- APK 暂不打包,因此本次交付物是源码、接口封装、页面和可验证开发版本,不包含安装包。
|
||||
|
||||
## 10. 需求覆盖矩阵(代码审计结果)
|
||||
|
||||
下表区分“代码已实现”和“真实环境已交付”,避免把静态测试或本地构建误认为部署、安装或培训完成。
|
||||
|
||||
| 序号 | 需求 | 当前结论 | 主要证据 |
|
||||
| ---: | --- | --- | --- |
|
||||
| 1 | 业务流程、功能边界、角色、交付内容和不包含事项 | 已确认 | 本设计第 1~3、8~9 节 |
|
||||
| 2 | 托盘、菌种、产线、仓库、装填、发货、使用、退回字段 | 已梳理 | `module_business/entity`、SQL 脚本 |
|
||||
| 3 | 后台、接口、数据库结构和基础运行环境 | 代码已完成;实际环境未部署 | `module_business`、`sql/`、前后端工程 |
|
||||
| 4 | 菌种、产线、仓库、用户等基础资料维护 | 已实现 | Web `views/master`、系统用户/角色模块 |
|
||||
| 5 | 托盘唯一标识、固定二维码、状态和档案 | 已实现 | `biz_pallet` 唯一约束、状态机、托盘管理页 |
|
||||
| 6 | 入库登记、入库单、库存更新 | 已实现 | `inbound_controller.py`、`inbound_service.py` |
|
||||
| 7 | 扫码装填菌种、批次、规格、包数并更新状态 | 已实现 | `filling_service.py`、Web/PDA 装填页 |
|
||||
| 8 | 发货单、托盘扫码/选择、明细和库存扣减 | 已实现 | `shipment_service.py`、Web/PDA 发货页 |
|
||||
| 9 | PDA 登录、扫码识别和读取菌种/余量 | 已实现;未打包 APK | `shierfeng-fastapi-app/src/pages/business`、PDA API |
|
||||
| 10 | 客户扫码、产线绑定、使用包数、操作人和时间 | 已实现 | `usage_service.py`、使用记录模型和页面 |
|
||||
| 11 | 全部/部分使用、剩余包数、退回和状态流转 | 已实现 | `pallet_state_machine.py`、使用/退回服务 |
|
||||
| 12 | 六类库存、装填、发货、消耗、退回、追溯报表 | 已实现 | `report_controller.py`、Web 六类报表页 |
|
||||
| 13 | 权限、操作记录、流转追溯和异常留痕 | 已实现 | 权限依赖、`Log`、`biz_pallet_event`、追溯报表 |
|
||||
| 14 | Android 页面、接口、PDA 适配和 APK | 页面/API/HID 已实现;APK 按要求暂缓 | PDA H5 构建与扫码组件 |
|
||||
| 15 | 后台、接口、PDA、库存、退回整体联调 | 代码级验证完成;真实设备联调未完成 | 契约测试通过;缺少真实设备/服务地址 |
|
||||
| 16 | 服务器、数据库、后台/接口服务和访问地址部署 | 未完成 | 缺少目标服务器和部署授权 |
|
||||
| 17 | App 安装、扫码验收和客户培训 | 未完成 | 缺少 PDA 设备及培训安排 |
|
||||
|
||||
登录页默认只填充用户名 `admin`;密码保持空白并由操作员手工输入,不将真实密码写入源码、测试或日志。
|
||||
@@ -0,0 +1,298 @@
|
||||
# 测试数据批次隔离与级联清理设计
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前菌种、客户、客户现场、产线、仓库和托盘等主数据在被业务单据引用后不允许删除。该约束能保护正式业务的外键完整性、库存状态、报表口径和托盘追溯链,但也导致开发、联调和验收期间产生的测试数据无法安全清理。
|
||||
|
||||
本设计引入“测试数据批次”,将正式数据与测试数据从创建、查询、业务操作到报表统计全程隔离。超级管理员可在预览完整影响范围后整批清理测试数据;正式数据在任何情况下都不能通过该能力物理删除。
|
||||
|
||||
## 2. 目标与非目标
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
- 以测试批次为边界隔离正式数据和测试数据。
|
||||
- 支持授权的普通用户在指定测试批次内完成入库、装填、发货、使用和退回流程。
|
||||
- 支持超级管理员将升级前已有的旧测试数据完整纳入测试批次。
|
||||
- 支持关闭批次后预览影响范围,并在单个数据库事务中整批清理。
|
||||
- 保留批次元数据和操作审计,但不保留已清理的测试业务明细。
|
||||
- 防止前端绕过、权限误配、并发请求或部分失败导致正式数据被删或测试链路残缺。
|
||||
|
||||
### 2.2 非目标
|
||||
|
||||
- 不允许按单条主数据或单据强制级联删除。
|
||||
- 不允许将正在进行的批次直接清理。
|
||||
- 不用测试清理代替正式业务的停用、作废、冲销或审计流程。
|
||||
- 不根据名称、编码、创建人或时间自动猜测旧数据是否为测试数据。
|
||||
- 不在本次实施中直接运行生产数据库迁移或清理。
|
||||
|
||||
## 3. 角色与权限边界
|
||||
|
||||
| 能力 | 超级管理员 | 已授权测试用户 | 其他用户 |
|
||||
| --- | --- | --- | --- |
|
||||
| 查看测试批次管理 | 允许 | 不允许 | 不允许 |
|
||||
| 创建、关闭、重新开启批次 | 允许 | 不允许 | 不允许 |
|
||||
| 授权批次测试用户 | 允许 | 不允许 | 不允许 |
|
||||
| 创建批次内测试主数据 | 允许 | 不允许 | 不允许 |
|
||||
| 进入已授权批次执行业务 | 允许 | 允许 | 不允许 |
|
||||
| 将旧数据纳入批次 | 允许 | 不允许 | 不允许 |
|
||||
| 预览并整批清理 | 允许 | 不允许 | 不允许 |
|
||||
| 物理删除正式数据 | 不允许 | 不允许 | 不允许 |
|
||||
|
||||
超级管理员以后端 `current_user.user.admin` 为最终判断,不仅依赖前端角色或菜单权限。测试批次接口可同时使用独立权限字符控制菜单展示,但权限字符被误分配给普通角色时,后端仍必须返回 403。
|
||||
|
||||
## 4. 数据模型
|
||||
|
||||
### 4.1 测试批次
|
||||
|
||||
新增 `biz_test_batch`,至少包含:
|
||||
|
||||
- `batch_id`:主键。
|
||||
- `batch_code`:系统生成的唯一编码。
|
||||
- `batch_name`:用于界面识别与二次确认。
|
||||
- `status`:`ACTIVE`、`CLOSED`、`PURGED`。
|
||||
- `create_by`、`create_time`、`update_by`、`update_time`。
|
||||
- `closed_by`、`closed_at`、`purged_by`、`purged_at`。
|
||||
- `purge_summary`:保留按数据类型汇总的清理数量,不保留敏感业务内容。
|
||||
- `remark`:批次用途说明。
|
||||
|
||||
新增 `biz_test_batch_user`,保存批次与参与测试用户的关联。该关联只是批次的外层访问权;现有用户-仓库、用户-现场数据范围仍然生效。
|
||||
|
||||
### 4.2 业务数据标记
|
||||
|
||||
以下表新增可空 `test_batch_id` 和索引,并外键引用 `biz_test_batch.batch_id`:
|
||||
|
||||
- 主数据:`biz_strain`、`biz_customer`、`biz_customer_site`、`biz_production_line`、`biz_warehouse`、`biz_pallet`。
|
||||
- 入库:`biz_inbound_order`、`biz_inbound_item`。
|
||||
- 装填:`biz_filling_record`。
|
||||
- 发货:`biz_shipment_order`、`biz_shipment_item`。
|
||||
- 使用:`biz_usage_record`。
|
||||
- 退回:`biz_return_order`、`biz_return_item`。
|
||||
- 库存与追溯:`biz_pallet_inventory`、`biz_pallet_event`。
|
||||
|
||||
`test_batch_id IS NULL` 表示正式数据;非空表示所属测试批次。该字段不允许普通业务请求直接写入,由后端从已验证的当前模式或关联记录中派生。
|
||||
|
||||
`biz_user_warehouse` 和 `biz_user_site` 不重复保存 `test_batch_id`,其数据归属由所关联的仓库或现场派生。批次清理时,必须先删除指向该批次仓库和现场的用户关联。
|
||||
|
||||
### 4.3 一致性约束
|
||||
|
||||
- 正式数据只能引用正式数据。
|
||||
- 测试数据只能引用同一批次的测试数据。
|
||||
- 不同测试批次之间不能互相引用。
|
||||
- 批次标记一旦创建不能通过通用编辑接口修改。
|
||||
- 只有“纳入测试批次”专用流程可将旧的无标记数据一次性改为测试数据。
|
||||
- 关联对象的批次一致性由服务层在写入前校验,数据库外键作为存在性和删除顺序的最后保护。
|
||||
- 现有编码、单号和二维码唯一约束继续全局生效;测试数据使用独立编码,清理后可重新使用。
|
||||
|
||||
## 5. 数据模式与查询隔离
|
||||
|
||||
### 5.1 模式选择
|
||||
|
||||
Web 管理端和 PDA 操作端都需支持:
|
||||
|
||||
- 正式模式:不传测试批次上下文,后端只允许 `test_batch_id IS NULL` 的数据。
|
||||
- 测试模式:携带当前 `batch_id`。已授权用户只能进入 `ACTIVE` 批次;超级管理员可在批次管理页以只读方式检查 `CLOSED` 批次。任何写入都要求批次为 `ACTIVE`。
|
||||
|
||||
前端可通过统一请求头传递当前批次,但该值只是请求上下文,不是信任根。后端必须每次验证用户授权、批次状态和数据归属。
|
||||
|
||||
### 5.2 隔离范围
|
||||
|
||||
批次条件必须应用于:
|
||||
|
||||
- 六类基础资料的列表、详情和选项。编码、单号和二维码的唯一性校验仍按全局数据执行,与现有数据库唯一约束保持一致。
|
||||
- 入库、装填、发货、使用和退回的列表、详情、扫码、创建、修改和确认。
|
||||
- 仓库、现场、产线、菌种和托盘等业务选项接口。
|
||||
- 托盘库存、装填、发货、消耗、退回和追溯六类报表。
|
||||
- 首页仓储运营指标、待处理数量、趋势图和状态分布。
|
||||
|
||||
任何缺失批次条件的查询都可能造成测试数据污染正式报表,实施时必须对所有业务 DAO 做完整盘点。
|
||||
|
||||
## 6. 批次生命周期
|
||||
|
||||
```text
|
||||
ACTIVE --关闭--> CLOSED --重新开启--> ACTIVE
|
||||
|
|
||||
+--预览并确认清理--> PURGED
|
||||
```
|
||||
|
||||
- `ACTIVE`:允许超级管理员创建测试主数据,允许已授权用户执行业务。
|
||||
- `CLOSED`:所有测试数据只读;可查看、预览清理范围、重新开启或清理。
|
||||
- `PURGED`:批次业务数据已删除;仅保留批次元数据和审计汇总,不能重新开启。
|
||||
|
||||
关闭、重新开启和清理都需锁定批次记录。每个测试写操作在业务数据加锁前必须重新校验批次仍为 `ACTIVE`,保证“关闭成功后无新写入”。
|
||||
|
||||
## 7. 新测试数据创建与业务继承
|
||||
|
||||
1. 超级管理员创建批次,分配参与测试的用户。
|
||||
2. 超级管理员切换到该批次,创建菌种、客户、现场、产线、仓库和托盘。
|
||||
3. 后端根据当前批次自动写入 `test_batch_id`,忽略或拒绝请求体中伪造的批次字段。
|
||||
4. 已授权用户进入该批次,使用批次内的仓库、现场、产线、菌种和托盘执行业务。
|
||||
5. 入库单、装填记录、发货单、使用记录、退回单、库存和事件由后端自动继承同一 `test_batch_id`。
|
||||
6. 任一关联数据的批次不一致时,整个业务操作失败并回滚。
|
||||
|
||||
## 8. 旧数据纳入测试批次
|
||||
|
||||
### 8.1 入口
|
||||
|
||||
超级管理员在正式模式的基础资料和业务管理页选择旧数据后,可执行“纳入测试批次”。报表页不提供该操作,因为报表行不是独立业务实体;需从其来源主数据或单据发起。
|
||||
|
||||
### 8.2 关联闭包
|
||||
|
||||
后端必须沿实际外键与业务关联双向计算连通闭包,直到没有新关联记录。闭包至少覆盖:
|
||||
|
||||
- 菌种与装填记录。
|
||||
- 客户、现场与产线层级。
|
||||
- 仓库、入库、装填、发货和退回。
|
||||
- 托盘、单据明细、库存、使用记录和追溯事件。
|
||||
- 与装填周期相关的发货、使用和退回记录。
|
||||
|
||||
由于旧数据没有可靠的测试标记,系统无法自动识别闭包中哪些数据被用户视为“正式”。预览必须按表展示数量和关键编号;如果闭包包含需保留的正式数据,管理员必须取消,系统不允许只标记半条关联链。
|
||||
|
||||
### 8.3 确认与写入
|
||||
|
||||
- 目标批次必须为 `ACTIVE`。
|
||||
- 闭包中所有数据必须尚未归属任何测试批次。
|
||||
- 管理员需输入目标批次名称二次确认。
|
||||
- 服务端使用不透明预览校验标识;确认时重新计算闭包,范围变化则拒绝旧确认。
|
||||
- 事务内锁定批次和闭包中的根记录,然后一次性写入所有 `test_batch_id`。
|
||||
- 任何一步失败都回滚,不得出现部分数据已纳入、部分数据仍为正式的状态。
|
||||
|
||||
## 9. 整批清理
|
||||
|
||||
### 9.1 前置条件
|
||||
|
||||
- 操作人必须是超级管理员。
|
||||
- 批次必须为 `CLOSED`。
|
||||
- 清理预览需展示每类主数据、单据、明细、库存和事件的数量。
|
||||
- 管理员必须输入完整批次名称并通过最终确认。
|
||||
- 确认请求必须携带服务端生成的一次性预览标识。
|
||||
|
||||
### 9.2 事务与删除顺序
|
||||
|
||||
清理服务在单个数据库事务内锁定批次,重新计算影响范围并校验预览标识。删除顺序必须由实际外键图生成并以回归测试固化,原则为:
|
||||
|
||||
1. 先删除 `biz_pallet_inventory`,解除其对托盘事件和活动装填记录的外键引用。
|
||||
2. 删除 `biz_usage_record`、`biz_return_item`、`biz_shipment_item` 和 `biz_inbound_item`。
|
||||
3. 删除 `biz_return_order`、`biz_shipment_order` 和 `biz_inbound_order`。
|
||||
4. 删除 `biz_pallet_event`,再删除 `biz_filling_record`。
|
||||
5. 删除指向测试仓库、现场的 `biz_user_warehouse` 和 `biz_user_site` 关联。
|
||||
6. 先删除产线,再删除现场和客户;随后删除仓库、菌种和托盘等其他主数据。
|
||||
7. 删除批次用户授权,更新批次为 `PURGED`,写入清理汇总。
|
||||
|
||||
实施时以 SQLAlchemy 模型和 MySQL/PostgreSQL 外键的实际编译结果为准,不依赖上述文字顺序代替自动化测试。
|
||||
|
||||
不对业务外键配置 `ON DELETE CASCADE`。如果任何外键、并发状态或数据库错误导致删除失败,整个事务回滚,批次保持 `CLOSED`。
|
||||
|
||||
### 9.3 幂等与审计
|
||||
|
||||
- 同一批次的并发清理只有一个可以执行,其他请求等待后读取最新状态。
|
||||
- 对 `PURGED` 批次重复请求返回稳定的“已清理”结果,不重复执行删除。
|
||||
- 批次记录保留清理人、清理时间和分类数量。
|
||||
- 系统操作日志保留,作为审计证据;日志不记录密码、Token、Cookie 或大量业务明细。
|
||||
|
||||
## 10. 界面与交互
|
||||
|
||||
### 10.1 测试数据批次管理
|
||||
|
||||
在“系统管理”下新增“测试数据批次”页面,仅超级管理员可见。页面支持:
|
||||
|
||||
- 查看批次编码、名称、状态、创建人、参与用户和时间。
|
||||
- 创建批次、编辑备注、授权或移除测试用户。
|
||||
- 关闭或重新开启未清理批次。
|
||||
- 查看数据汇总、清理预览和最终清理确认。
|
||||
- 查看已清理批次的审计汇总。
|
||||
|
||||
### 10.2 模式提示
|
||||
|
||||
- Web 顶部和 PDA 工作台提供“正式数据 / 已授权测试批次”切换。
|
||||
- 测试模式始终展示高可见的批次名称和“测试数据”标识,不得只在隐蔽的下拉框中表示。
|
||||
- 切换模式后刷新当前列表、选项、首页指标和报表,并清空未提交的跨模式表单状态。
|
||||
- 批次被关闭、授权被移除或会话失效时,普通测试用户退回正式模式并得到明确提示;超级管理员仍可从批次管理页查看关闭批次的只读汇总和清理预览。
|
||||
|
||||
### 10.3 现有删除入口
|
||||
|
||||
- 基础资料、业务管理和报表页不再提供物理删除入口。
|
||||
- 正式主数据通过停用保留历史引用;正式业务单据按现有业务状态处理,不使用测试批次能力物理删除。
|
||||
- 旧的删除 API 保留路由兼容,但统一拒绝物理删除并提示“请通过测试数据批次清理”。
|
||||
- “纳入测试批次”只在正式模式下向超级管理员展示。
|
||||
|
||||
## 11. 后端组件与接口边界
|
||||
|
||||
建议新增独立的批次控制器、服务、DAO 和 VO,不将级联清理逻辑分散到六个主数据服务中。职责划分如下:
|
||||
|
||||
- 批次管理:创建、列表、详情、用户授权、关闭和重新开启。
|
||||
- 批次上下文:解析请求模式,校验用户与批次,为查询和写入提供受信任的 `batch_id | None`。
|
||||
- 批次一致性校验:根据业务输入校验所有关联数据处于同一模式和批次。
|
||||
- 旧数据纳入:计算关联闭包、生成预览、校验确认并原子写入批次标记。
|
||||
- 整批清理:统计影响数据、生成预览、校验确认并原子删除。
|
||||
|
||||
预览与执行必须分为不同接口。执行接口不接受前端传入的“要删哪些表或 ID”,只接受批次 ID、批次名称确认和一次性预览标识;实际范围始终由后端重算。
|
||||
|
||||
## 12. 错误处理
|
||||
|
||||
需向用户明确区分以下失败:
|
||||
|
||||
- 非超级管理员访问管理或清理接口。
|
||||
- 用户未被授权访问所选测试批次。
|
||||
- 批次已关闭、已清理或在操作期间状态发生变化。
|
||||
- 请求将正式数据与测试数据或不同批次数据混用。
|
||||
- 旧数据关联闭包包含已属于其他批次的数据。
|
||||
- 预览后数据范围发生变化,一次性校验标识失效。
|
||||
- 批次名称确认不匹配。
|
||||
- 外键、并发或数据库错误触发整体回滚。
|
||||
|
||||
前端不将未知错误表示为成功,也不在失败后乐观移除列表记录。所有成功状态均以服务端提交后重新查询的结果为准。
|
||||
|
||||
## 13. 数据库迁移与发布
|
||||
|
||||
- 同步更新 MySQL 和 PostgreSQL 全量建库脚本。
|
||||
- 为已有环境提供 MySQL 和 PostgreSQL 增量补丁。
|
||||
- 先创建批次与授权表,再以可空字段方式增加 `test_batch_id`、索引和外键。
|
||||
- 既有记录的 `test_batch_id` 保持 `NULL`,迁移不自动更新业务数据。
|
||||
- 发布前必须完成数据库备份和恢复演练;应用迁移和首次旧数据纳入属于独立高风险操作,需单独确认后执行。
|
||||
- 先发布后端与前端代码,确认正式模式查询不回归,再创建新测试批次或纳入旧数据。
|
||||
|
||||
## 14. 测试与验证
|
||||
|
||||
### 14.1 后端
|
||||
|
||||
- 批次生命周期和不合法状态转换。
|
||||
- 超级管理员硬校验,包括普通用户持有误分配权限字符的情况。
|
||||
- 批次用户授权与现有仓库/现场数据范围的叠加效果。
|
||||
- 正式、同批次和跨批次关联校验。
|
||||
- 创建主数据和五类业务时的批次自动继承。
|
||||
- 主数据、业务列表、选项、扫码、首页和六类报表的模式隔离。
|
||||
- 旧数据闭包的完整性、循环去重、跨批次拒绝和事务回滚。
|
||||
- 关闭批次后所有写入被拒绝,重新开启后恢复。
|
||||
- 清理预览计数、预览过期、确认文本、外键删除顺序、失败回滚和重复清理幂等性。
|
||||
- MySQL 和 PostgreSQL 的 SQL 编译与迁移契约。
|
||||
|
||||
### 14.2 Web 与 PDA
|
||||
|
||||
- 超级管理员、已授权测试用户和未授权用户的菜单与按钮可见性。
|
||||
- 正式/测试模式切换、批次标识、请求上下文和授权失效回退。
|
||||
- 旧数据纳入预览、批次名称二次确认和预览过期提示。
|
||||
- 批次关闭后的只读界面和清理确认。
|
||||
- 现有物理删除按钮移除,直接调用旧删除 API 时的稳定拒绝提示。
|
||||
- Web 生产构建、PDA H5 构建、最相关契约测试和后端业务单元测试。
|
||||
|
||||
## 15. 验收标准
|
||||
|
||||
1. 正式模式的页面、选项、首页和报表不展示任何测试数据。
|
||||
2. 测试模式只展示当前批次数据,不显示正式数据或其他批次数据。
|
||||
3. 授权的普通用户可完成测试业务链,但无法管理、纳入、关闭或清理批次。
|
||||
4. 正式与测试数据、不同测试批次之间不能混用。
|
||||
5. 超级管理员可预览并完整纳入旧测试数据,范围变化时旧确认失效。
|
||||
6. `ACTIVE` 批次不能清理;`CLOSED` 批次不能新增或修改业务数据。
|
||||
7. 整批清理成功后,所有相关主数据、单据、明细、库存和追溯记录消失,报表不再返回该批次数据。
|
||||
8. 清理任一步失败时不删除任何数据,批次仍为 `CLOSED`。
|
||||
9. 超级管理员也无法通过批次清理或旧删除 API 物理删除正式数据。
|
||||
10. 已清理批次保留操作人、时间和分类数量,且日志不包含凭据或敏感信息。
|
||||
|
||||
## 16. 已知风险与控制
|
||||
|
||||
- **旧数据闭包可能过大**:旧测试数据如果复用了正式仓库、现场或托盘,关联闭包可能扩展到大量正式数据。系统必须展示完整预览并允许取消,不提供忽略关联的强制选项。
|
||||
- **大批量事务时间较长**:在目标数据库上通过压力测试确定在线清理上限,并将上限写入部署配置和运维文档。在线接口必须阻止超过上限的批次,且不得通过分段提交规避原子性;超大批次由 DBA 在备份后使用受审计脚本处理。数值上限依赖目标环境,本设计不在缺少实测数据时猜测固定值。
|
||||
- **请求上下文被伪造**:后端每次验证授权、批次状态和数据归属,不相信前端显示或请求头本身。
|
||||
- **漏加查询条件导致统计污染**:使用统一批次上下文和 DAO 查询辅助方法,并用契约测试盘点全部业务查询。
|
||||
- **仅前端隐藏不能提供安全性**:批次管理、纳入和清理均在后端强制超级管理员检查。
|
||||
- **迁移与实际数据存在差异**:应用前在目标数据库执行只读外键和表结构盘点,与代码模型和迁移脚本三方对照,不假设生产结构与仓库 SQL 完全一致。
|
||||
@@ -0,0 +1,54 @@
|
||||
# 测试数据批次操作说明
|
||||
|
||||
测试数据采用批次隔离。正式数据的 `test_batch_id` 为 `NULL`,测试数据必须归属于一个 `biz_test_batch` 批次。系统不会因为安装迁移脚本而自动把历史数据标记为测试数据;历史数据需要由超级管理员逐项预览并纳入批次。
|
||||
|
||||
## 权限边界
|
||||
|
||||
- 批次创建、编辑、授权、关闭/重新开启、旧数据纳入和批次清理均由后端校验超级管理员身份。
|
||||
- 普通用户不能通过前端隐藏、菜单权限或直接调用接口绕过后端校验。
|
||||
- 普通用户只能看到被授权且状态为 `ACTIVE` 的批次,并在 PDA 或 Web 测试模式中使用该批次。
|
||||
- 物理删除不再通过菌种、客户、现场、产线、仓库、托盘、入库和发货页面提供。清理必须以批次为单位执行。
|
||||
|
||||
## 上线前准备
|
||||
|
||||
1. 先备份数据库,并确认当前没有正在进行的入库、装填、发货、使用或退回操作。
|
||||
2. MySQL 执行 `shierfeng-fastapi-backend/sql/patches/20260922_test_data_batch_mysql.sql`。
|
||||
3. 迁移完成后检查 `biz_test_batch`、`biz_test_batch_user` 和各业务表的 `test_batch_id` 字段及外键。
|
||||
4. 配置在线清理上限。默认 `TEST_BATCH_ONLINE_PURGE_MAX_ROWS=0`,表示禁止在线清理;只有明确配置为正整数且批次影响总数不超过上限时,管理页才允许执行清理。预览令牌默认有效期为 `TEST_BATCH_PREVIEW_TTL_SECONDS=600` 秒。
|
||||
|
||||
迁移脚本是一次性增量脚本,重复执行前必须由数据库管理员确认目标表和字段是否已经存在。本文档不替代备份、变更审批和回滚预案。
|
||||
|
||||
## 新建并授权批次
|
||||
|
||||
1. 超级管理员登录 Web 管理端,进入“系统管理 -> 测试数据批次”,创建批次并记录批次名称。
|
||||
2. 在“用户”操作中填写需要参与测试的用户 ID。保存时会完整替换该批次的授权用户列表;留空表示取消全部授权。
|
||||
3. 用户在 PDA 工作台选择“测试”并选择已授权的 ACTIVE 批次。切换模式会重新打开工作台,清空当前页面未提交的表单和扫码状态。
|
||||
4. 测试模式下的请求自动携带 `X-Test-Batch-Id`。批次关闭、清理、撤销授权或失效后,客户端会清除本地批次并回到正式模式。
|
||||
|
||||
## 将历史数据纳入批次
|
||||
|
||||
历史数据默认仍是正式数据。只有超级管理员可以在菌种、客户、现场、产线、仓库、托盘以及五类业务页面选择根记录,点击“纳入测试批次”。
|
||||
|
||||
纳入操作分为两步:
|
||||
|
||||
1. **预览**:系统根据根记录计算完整关联闭包,展示将被标记的记录和数量,并生成短时有效的预览令牌。
|
||||
2. **执行**:管理员核对批次名称和影响范围后提交。服务端会重新加锁并计算闭包;范围变化、记录已归属于其他批次、批次关闭或令牌过期时,整个操作失败并回滚。
|
||||
|
||||
不要直接修改业务表的 `test_batch_id`。这样会跳过关联闭包、并发校验和审计记录,可能造成跨批次数据无法清理。
|
||||
|
||||
## 关闭与清理批次
|
||||
|
||||
1. 批次完成测试后先点击“关闭”。关闭后不能继续写入该批次,但可以重新开启;重新开启会使已有的清理预览失效。
|
||||
2. 对 CLOSED 批次点击“清理”,先查看预览中的分类、数量和编号。
|
||||
3. 只有在线清理上限允许时,输入完整批次名称并确认执行。服务端会校验预览令牌、批次名称、影响范围和当前行数,然后按依赖顺序在一个事务中删除批次闭包。
|
||||
4. 成功后批次状态变为 `PURGED`,保留清理时间、执行人和数量摘要;已清理批次不可重新开启或再次写入。
|
||||
|
||||
清理是不可逆的数据删除操作。影响总数超过在线上限时,应按运维审批流程执行离线清理,不要通过 SQL 手工绕过系统约束。
|
||||
|
||||
## 验证清单
|
||||
|
||||
- 正式模式看不到 `test_batch_id` 非空的测试记录。
|
||||
- 普通用户无法看到批次管理页的管理操作,也无法调用纳入和清理接口。
|
||||
- PDA 测试模式的请求带有正确批次 ID,退出登录后本地批次缓存被清除。
|
||||
- 关闭批次后新建和确认操作被拒绝;清理预览与实际执行之间的数据变化会使执行失败而不是误删。
|
||||
- 清理完成后,批次闭包中的主数据、单据、明细、库存和事件记录数量与审计摘要一致。
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version" : "1.0",
|
||||
"configurations" : [
|
||||
{
|
||||
"customPlaygroundType" : "device",
|
||||
"packageName" : "",
|
||||
"playground" : "standard",
|
||||
"type" : "uni-app:app-android"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -23,6 +23,18 @@
|
||||
>
|
||||
> 另外谨慎升级 `package.json` 中锁定的 `pinia`/`vue`/`@vue/*` 相关包的版本,新版本可能 `uni-app` 没有兼容,造成一些奇怪的 bug
|
||||
|
||||
### 配置 PDA 后端地址
|
||||
|
||||
PDA 默认请求本机 `http://localhost:9099`,仅适用于后端运行在同一设备的本地开发。部署到独立 PDA 或局域网服务器时,在构建环境注入 `VITE_APP_BASE_API`:
|
||||
|
||||
```bash
|
||||
VITE_APP_BASE_API=http://<服务器地址>:9099 pnpm run build:h5
|
||||
```
|
||||
|
||||
不要把包含真实凭据的环境文件或地址配置提交到仓库。
|
||||
|
||||
PDA 测试模式、批次授权和失效回退规则请阅读[测试数据批次操作说明](../docs/test-data-batch-operations.md)。PDA 只允许选择后端返回的 ACTIVE 授权批次,退出登录会清除本地批次上下文。
|
||||
|
||||
### vscode
|
||||
|
||||
使用 `vscode` 的开发者,请先安装 [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) 智能提示与感应插件
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/static/favicon.ico" />
|
||||
<script>
|
||||
var coverSupport =
|
||||
'CSS' in window &&
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"version": "1.9.0",
|
||||
"packageManager": "pnpm@10.28.1",
|
||||
"scripts": {
|
||||
"test:business": "node --test tests/business-contract.test.mjs",
|
||||
"dev": "uni -p mp-weixin",
|
||||
"build": "npm run build:mp-weixin",
|
||||
"dev:app": "uni -p app",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listScanExceptions() {
|
||||
return request({
|
||||
url: "/business/dashboard/scan-exceptions",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listFilling(query) {
|
||||
return request({
|
||||
url: "/business/filling/list",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getFillingStrainOptions() {
|
||||
return request({
|
||||
url: "/business/filling/strain-options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function scanFillingPallet(qrToken) {
|
||||
return request({
|
||||
url: "/business/filling/scan/" + qrToken,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function getFilling(fillingId) {
|
||||
return request({
|
||||
url: "/business/filling/" + fillingId,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function addFilling(data) {
|
||||
return request({
|
||||
url: "/business/filling",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listInbound(query) {
|
||||
return request({
|
||||
url: "/business/inbound/list",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getInboundWarehouseOptions() {
|
||||
return request({
|
||||
url: "/business/inbound/warehouse-options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function scanInboundPallet(qrToken, query) {
|
||||
return request({
|
||||
url: "/business/inbound/scan/" + qrToken,
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getInbound(inboundId) {
|
||||
return request({
|
||||
url: "/business/inbound/" + inboundId,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function addInbound(data) {
|
||||
return request({
|
||||
url: "/business/inbound",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateInbound(data) {
|
||||
return request({
|
||||
url: "/business/inbound",
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmInbound(inboundId, data) {
|
||||
return request({
|
||||
url: "/business/inbound/" + inboundId + "/confirm",
|
||||
method: "put",
|
||||
data,
|
||||
headers: { repeatSubmit: false },
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listReturn(query) {
|
||||
return request({
|
||||
url: "/business/return/list",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getReturnWarehouseOptions() {
|
||||
return request({
|
||||
url: "/business/return/warehouse-options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function scanReturnPallet(qrToken, query) {
|
||||
return request({
|
||||
url: "/business/return/scan/" + qrToken,
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function requestReturn(data) {
|
||||
return request({
|
||||
url: "/business/return/request",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function getReturn(returnId) {
|
||||
return request({
|
||||
url: "/business/return/" + returnId,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmReturn(returnId, data) {
|
||||
return request({
|
||||
url: "/business/return/" + returnId + "/confirm",
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listShipment(query) {
|
||||
return request({
|
||||
url: "/business/shipment/list",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getShipmentWarehouseOptions() {
|
||||
return request({
|
||||
url: "/business/shipment/warehouse-options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function getShipmentSiteOptions() {
|
||||
return request({
|
||||
url: "/business/shipment/site-options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function scanShipmentPallet(qrToken, query) {
|
||||
return request({
|
||||
url: "/business/shipment/scan/" + qrToken,
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getShipment(shipmentId) {
|
||||
return request({
|
||||
url: "/business/shipment/" + shipmentId,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function addShipment(data) {
|
||||
return request({
|
||||
url: "/business/shipment",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateShipment(data) {
|
||||
return request({
|
||||
url: "/business/shipment",
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmShipment(shipmentId, data) {
|
||||
return request({
|
||||
url: "/business/shipment/" + shipmentId + "/confirm",
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listUsage(query) {
|
||||
return request({
|
||||
url: "/business/usage/list",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getUsageLineOptions(siteId) {
|
||||
return request({
|
||||
url: "/business/usage/line-options",
|
||||
method: "get",
|
||||
params: { siteId },
|
||||
});
|
||||
}
|
||||
|
||||
export function scanUsagePallet(qrToken) {
|
||||
return request({
|
||||
url: "/business/usage/scan/" + qrToken,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function getUsage(usageId) {
|
||||
return request({
|
||||
url: "/business/usage/" + usageId,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function addUsage(data) {
|
||||
return request({
|
||||
url: "/business/usage",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listPalletInventory(query) {
|
||||
return request({
|
||||
url: "/report/pallet-inventory/list",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function listShipmentReport(query) {
|
||||
return request({
|
||||
url: "/report/shipment/list",
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// PDA 只读取当前登录用户被授权且仍处于 ACTIVE 状态的批次。
|
||||
export function listAvailableTestBatches() {
|
||||
return request({
|
||||
url: "/system/test-batch/options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<view class="w-full">
|
||||
<view class="flex items-center rounded-xl bg-white p-2 shadow-sm">
|
||||
<input
|
||||
v-model="scanValue"
|
||||
class="h-11 min-w-0 flex-1 px-3 text-sm text-gray-700"
|
||||
type="text"
|
||||
confirm-type="done"
|
||||
maxlength="128"
|
||||
:focus="focusRequested"
|
||||
:disabled="disabled || loading"
|
||||
placeholder="请扫描或输入托盘二维码后按回车"
|
||||
@confirm="handleSubmit"
|
||||
@input="clearError"
|
||||
/>
|
||||
<button
|
||||
class="ml-2 h-10 rounded-lg bg-blue-500 px-3 text-xs text-white active:opacity-80 disabled:bg-gray-300"
|
||||
:disabled="disabled || loading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ loading ? "校验中" : "提交扫描" }}
|
||||
</button>
|
||||
</view>
|
||||
<text v-if="errorMessage" class="mt-2 block text-xs text-red-500">
|
||||
{{ errorMessage }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { nextTick, onMounted, ref, watch } from "vue";
|
||||
import { isDuplicateScan, normalizeScanValue } from "@/utils/palletScan";
|
||||
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
duplicateWindowMs: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["scan", "invalid", "duplicate"]);
|
||||
const scanValue = ref("");
|
||||
const errorMessage = ref("");
|
||||
const focusRequested = ref(false);
|
||||
let lastScanValue = "";
|
||||
let lastScanAt = 0;
|
||||
|
||||
function focusInput() {
|
||||
if (props.disabled || props.loading) return;
|
||||
focusRequested.value = false;
|
||||
nextTick(() => {
|
||||
if (!props.disabled && !props.loading) {
|
||||
focusRequested.value = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearError() {
|
||||
errorMessage.value = "";
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (props.disabled || props.loading) return;
|
||||
|
||||
let normalizedValue;
|
||||
try {
|
||||
normalizedValue = normalizeScanValue(scanValue.value);
|
||||
} catch (error) {
|
||||
errorMessage.value = error.message;
|
||||
emit("invalid", error.message);
|
||||
focusInput();
|
||||
return;
|
||||
}
|
||||
|
||||
const scannedAt = Date.now();
|
||||
if (
|
||||
isDuplicateScan(
|
||||
normalizedValue,
|
||||
lastScanValue,
|
||||
scannedAt,
|
||||
lastScanAt,
|
||||
props.duplicateWindowMs,
|
||||
)
|
||||
) {
|
||||
scanValue.value = "";
|
||||
emit("duplicate", normalizedValue);
|
||||
focusInput();
|
||||
return;
|
||||
}
|
||||
|
||||
lastScanValue = normalizedValue;
|
||||
lastScanAt = scannedAt;
|
||||
scanValue.value = "";
|
||||
errorMessage.value = "";
|
||||
emit("scan", normalizedValue);
|
||||
focusInput();
|
||||
}
|
||||
|
||||
onMounted(focusInput);
|
||||
|
||||
watch(
|
||||
() => [props.disabled, props.loading],
|
||||
([disabled, loading]) => {
|
||||
if (!disabled && !loading) focusInput();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<view v-if="testBatchStore.isTestMode" class="test-batch-banner">
|
||||
<view class="test-batch-icon">
|
||||
<view class="i-mdi-flask-outline test-batch-icon-glyph"></view>
|
||||
</view>
|
||||
<view class="test-batch-copy">
|
||||
<text class="test-batch-title">测试数据</text>
|
||||
<text class="test-batch-value">
|
||||
当前批次:{{ testBatchStore.currentBatchName || "已选测试批次" }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTestBatchStore } from "@/store";
|
||||
|
||||
const testBatchStore = useTestBatchStore();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.test-batch-banner {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
align-items: flex-start;
|
||||
gap: 12rpx;
|
||||
margin-top: 18rpx;
|
||||
border: 1rpx solid #f1b8b4;
|
||||
border-radius: 16rpx;
|
||||
padding: 16rpx;
|
||||
background: #fff5f4;
|
||||
}
|
||||
|
||||
.test-batch-icon {
|
||||
display: flex;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
flex: 0 0 52rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #d9534f;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.test-batch-icon-glyph {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.test-batch-copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.test-batch-title,
|
||||
.test-batch-value {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.test-batch-title {
|
||||
color: #b42318;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.test-batch-value {
|
||||
margin-top: 5rpx;
|
||||
color: #c2413b;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.55;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,352 @@
|
||||
<template>
|
||||
<view class="test-batch-switch">
|
||||
<view class="mode-switch-header">
|
||||
<view class="mode-switch-copy">
|
||||
<text class="mode-switch-title">数据模式</text>
|
||||
<text class="mode-switch-description">
|
||||
{{ testBatchStore.isTestMode ? "当前操作隔离在测试批次" : "当前使用正式数据" }}
|
||||
</text>
|
||||
</view>
|
||||
<text
|
||||
class="mode-switch-status"
|
||||
:class="testBatchStore.isTestMode ? 'is-test' : 'is-formal'"
|
||||
>
|
||||
{{ testBatchStore.isTestMode ? "测试数据" : "正式数据" }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="mode-switch-controls">
|
||||
<button
|
||||
class="mode-switch-button"
|
||||
:class="{ active: !testBatchStore.isTestMode }"
|
||||
:disabled="loading"
|
||||
size="mini"
|
||||
@click="handleModeClick('formal')"
|
||||
>
|
||||
正式
|
||||
</button>
|
||||
<button
|
||||
class="mode-switch-button"
|
||||
:class="{ 'active-test': testBatchStore.isTestMode }"
|
||||
:disabled="loading || !activeOptions.length"
|
||||
size="mini"
|
||||
@click="handleModeClick('test')"
|
||||
>
|
||||
测试
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="testBatchStore.isTestMode || activeOptions.length"
|
||||
class="test-batch-picker-row"
|
||||
>
|
||||
<text class="test-batch-picker-label-title">测试批次</text>
|
||||
<picker
|
||||
:range="activeOptions"
|
||||
range-key="batchName"
|
||||
:value="selectedBatchIndex"
|
||||
:disabled="loading || !activeOptions.length"
|
||||
@change="handleBatchChange"
|
||||
>
|
||||
<view class="test-batch-picker">
|
||||
<text class="test-batch-picker-label">
|
||||
{{ selectedBatch?.batchName || (loading ? "加载中..." : "暂无可用测试批次") }}
|
||||
</text>
|
||||
<view class="i-mdi-chevron-down test-batch-picker-arrow"></view>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view v-if="loadError" class="mode-switch-error">
|
||||
<text class="mode-switch-error-text">{{ loadError }}</text>
|
||||
<button
|
||||
class="mode-switch-retry"
|
||||
size="mini"
|
||||
:disabled="loading"
|
||||
@click="loadOptions"
|
||||
>
|
||||
重试
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useTestBatchStore } from "@/store";
|
||||
|
||||
const testBatchStore = useTestBatchStore();
|
||||
const loading = ref(false);
|
||||
const loadError = ref("");
|
||||
const activeOptions = computed(() =>
|
||||
testBatchStore.options.filter((item) => item.status === "ACTIVE"),
|
||||
);
|
||||
const selectedBatch = computed(() =>
|
||||
activeOptions.value.find(
|
||||
(item) => String(item.batchId) === String(testBatchStore.currentBatchId),
|
||||
),
|
||||
);
|
||||
const selectedBatchIndex = computed(() => {
|
||||
const index = activeOptions.value.findIndex(
|
||||
(item) => String(item.batchId) === String(testBatchStore.currentBatchId),
|
||||
);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
|
||||
function errorMessage(error) {
|
||||
return error?.response?.data?.msg || error?.message || "测试批次加载失败,请稍后重试";
|
||||
}
|
||||
|
||||
async function loadOptions() {
|
||||
loading.value = true;
|
||||
loadError.value = "";
|
||||
try {
|
||||
await testBatchStore.refreshOptions();
|
||||
} catch (error) {
|
||||
if (error?.reason !== "TEST_BATCH_UNAVAILABLE") {
|
||||
loadError.value = errorMessage(error);
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function confirmModeSwitch() {
|
||||
return new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title: "切换数据模式",
|
||||
content: "切换后会清空当前页面未提交的扫码和表单内容,确定继续吗?",
|
||||
cancelText: "取消",
|
||||
confirmText: "确定",
|
||||
success: (result) => resolve(result.confirm === true),
|
||||
fail: () => resolve(false),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function handleModeClick(mode) {
|
||||
if (loading.value) return;
|
||||
if (mode === "formal") {
|
||||
if (!testBatchStore.isTestMode || !(await confirmModeSwitch())) return;
|
||||
testBatchStore.leaveTestMode();
|
||||
uni.reLaunch({ url: "/pages/work/index" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (testBatchStore.isTestMode) return;
|
||||
const batch = activeOptions.value[0];
|
||||
if (!batch) {
|
||||
uni.showToast({ title: "暂无可用测试批次", icon: "none" });
|
||||
return;
|
||||
}
|
||||
if (!(await confirmModeSwitch())) return;
|
||||
try {
|
||||
testBatchStore.selectBatch(batch);
|
||||
uni.reLaunch({ url: "/pages/work/index" });
|
||||
} catch (error) {
|
||||
uni.showToast({ title: error.message || "测试批次不可用", icon: "none" });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBatchChange(event) {
|
||||
const batch = activeOptions.value[Number(event.detail.value)];
|
||||
if (!batch || String(batch.batchId) === String(testBatchStore.currentBatchId)) return;
|
||||
if (!(await confirmModeSwitch())) return;
|
||||
try {
|
||||
testBatchStore.selectBatch(batch);
|
||||
uni.reLaunch({ url: "/pages/work/index" });
|
||||
} catch (error) {
|
||||
uni.showToast({ title: error.message || "测试批次不可用", icon: "none" });
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadOptions);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.test-batch-switch {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
border: 1rpx solid #e5eeee;
|
||||
border-radius: 20rpx;
|
||||
padding: 22rpx;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 8rpx 24rpx rgba(40, 34, 92, 0.06);
|
||||
}
|
||||
|
||||
.mode-switch-header {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.mode-switch-copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mode-switch-title,
|
||||
.mode-switch-description,
|
||||
.test-batch-picker-label-title,
|
||||
.test-batch-picker-label,
|
||||
.mode-switch-error-text {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.mode-switch-title {
|
||||
color: #28225c;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.mode-switch-description {
|
||||
margin-top: 5rpx;
|
||||
color: #8b969d;
|
||||
font-size: 21rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.mode-switch-status {
|
||||
flex: 0 0 auto;
|
||||
border: 1rpx solid #dce7e7;
|
||||
border-radius: 999rpx;
|
||||
padding: 7rpx 12rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.mode-switch-status.is-formal {
|
||||
border-color: #dce7e7;
|
||||
background: #f5f8f8;
|
||||
color: #64727a;
|
||||
}
|
||||
|
||||
.mode-switch-status.is-test {
|
||||
border-color: #f1b8b4;
|
||||
background: #fff1f0;
|
||||
color: #b42318;
|
||||
}
|
||||
|
||||
.mode-switch-controls {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
gap: 10rpx;
|
||||
margin-top: 18rpx;
|
||||
border-radius: 12rpx;
|
||||
padding: 5rpx;
|
||||
background: #f1f5f5;
|
||||
}
|
||||
|
||||
.mode-switch-button {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
height: 64rpx;
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 9rpx;
|
||||
background: transparent;
|
||||
color: #748087;
|
||||
font-size: 23rpx;
|
||||
line-height: 64rpx;
|
||||
}
|
||||
|
||||
.mode-switch-button::after,
|
||||
.mode-switch-retry::after {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.mode-switch-button.active {
|
||||
background: #ffffff;
|
||||
box-shadow: 0 4rpx 10rpx rgba(40, 34, 92, 0.08);
|
||||
color: #28225c;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mode-switch-button.active-test {
|
||||
background: #d9534f;
|
||||
box-shadow: 0 4rpx 10rpx rgba(217, 83, 79, 0.18);
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.test-batch-picker-row {
|
||||
min-width: 0;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.test-batch-picker-label-title {
|
||||
color: #6c7786;
|
||||
font-size: 21rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.test-batch-picker {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12rpx;
|
||||
margin-top: 8rpx;
|
||||
border: 1rpx solid #e0eaea;
|
||||
border-radius: 12rpx;
|
||||
padding: 16rpx;
|
||||
background: #fbfdfd;
|
||||
}
|
||||
|
||||
.test-batch-picker-label {
|
||||
flex: 1;
|
||||
color: #47545c;
|
||||
font-size: 23rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.test-batch-picker-arrow {
|
||||
flex: 0 0 30rpx;
|
||||
color: #96a5a9;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.mode-switch-error {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
margin-top: 14rpx;
|
||||
border-radius: 10rpx;
|
||||
padding: 10rpx 12rpx;
|
||||
background: #fff8ed;
|
||||
}
|
||||
|
||||
.mode-switch-error-text {
|
||||
flex: 1;
|
||||
color: #a15c00;
|
||||
font-size: 21rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.mode-switch-retry {
|
||||
width: auto;
|
||||
min-width: 74rpx;
|
||||
height: 52rpx;
|
||||
flex: 0 0 auto;
|
||||
margin: 0;
|
||||
padding: 0 12rpx;
|
||||
border: 0;
|
||||
border-radius: 8rpx;
|
||||
background: #fff0d6;
|
||||
color: #a15c00;
|
||||
font-size: 20rpx;
|
||||
line-height: 52rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,7 @@
|
||||
// 应用全局配置
|
||||
export default {
|
||||
baseUrl: "http://localhost:9099",
|
||||
// PDA 部署到独立设备时由构建环境注入服务器地址;本地开发保留回环地址。
|
||||
baseUrl: import.meta.env.VITE_APP_BASE_API || "http://localhost:9099",
|
||||
// 应用信息
|
||||
appInfo: {
|
||||
// 应用名称
|
||||
@@ -8,7 +9,7 @@ export default {
|
||||
// 应用版本
|
||||
version: "1.9.0",
|
||||
// 应用logo
|
||||
logo: "/static/logo.png",
|
||||
logo: "/static/sylvan-logo.png",
|
||||
// 官方网站
|
||||
site_url: "https://vfadmin.insistence.tech",
|
||||
// 政策协议
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
export const businessMenuEntries = [
|
||||
{
|
||||
path: "/pages/business/inbound/index",
|
||||
title: "仓库入库",
|
||||
description: "扫描托盘登记入库",
|
||||
icon: "i-mdi-package-down",
|
||||
iconClass: "bg-blue-50 text-blue-600",
|
||||
permission: "business:inbound:list",
|
||||
},
|
||||
{
|
||||
path: "/pages/business/filling/index",
|
||||
title: "工厂装填",
|
||||
description: "录入菌种和包数",
|
||||
icon: "i-mdi-package-variant-closed-plus",
|
||||
iconClass: "bg-green-50 text-green-600",
|
||||
permission: "business:filling:list",
|
||||
},
|
||||
{
|
||||
path: "/pages/business/shipment/index",
|
||||
title: "发货出库",
|
||||
description: "扫码创建发货单",
|
||||
icon: "i-mdi-truck-delivery",
|
||||
iconClass: "bg-orange-50 text-orange-600",
|
||||
permission: "business:shipment:list",
|
||||
},
|
||||
{
|
||||
path: "/pages/business/usage/index",
|
||||
title: "客户现场使用",
|
||||
description: "绑定产线记录消耗",
|
||||
icon: "i-mdi-factory",
|
||||
iconClass: "bg-purple-50 text-purple-600",
|
||||
permission: "business:usage:list",
|
||||
},
|
||||
{
|
||||
path: "/pages/business/return/index",
|
||||
title: "退回仓库",
|
||||
description: "申请和确认退回",
|
||||
icon: "i-mdi-undo-variant",
|
||||
iconClass: "bg-red-50 text-red-600",
|
||||
permission: "business:return:list",
|
||||
},
|
||||
];
|
||||
@@ -5,10 +5,24 @@ import store from "./store"; // store
|
||||
import { install } from "./plugins"; // plugins
|
||||
import "./permission"; // permission
|
||||
import { useDict } from "@/utils/dict";
|
||||
import { useTestBatchStore } from "./store";
|
||||
|
||||
let testBatchUnavailableListenerRegistered = false;
|
||||
|
||||
function registerTestBatchUnavailableListener() {
|
||||
if (testBatchUnavailableListenerRegistered || typeof uni.$on !== "function") {
|
||||
return;
|
||||
}
|
||||
testBatchUnavailableListenerRegistered = true;
|
||||
uni.$on("test-batch-unavailable", () => {
|
||||
useTestBatchStore().leaveTestMode();
|
||||
});
|
||||
}
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App);
|
||||
app.use(store);
|
||||
registerTestBatchUnavailableListener();
|
||||
app.config.globalProperties.useDict = useDict;
|
||||
install(app);
|
||||
return {
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"name": "智链云枢管理系统",
|
||||
"appid": "__UNI__25A9D80",
|
||||
"description": "",
|
||||
"versionName": "1.9.0",
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
"app-plus": {
|
||||
"usingComponents": true,
|
||||
"nvueCompiler": "uni-app",
|
||||
"splashscreen": {
|
||||
"alwaysShowBeforeRender": true,
|
||||
"waiting": true,
|
||||
"autoclose": true,
|
||||
"delay": 0
|
||||
"name" : "智链云枢管理系统",
|
||||
"appid" : "__UNI__F8DBA93",
|
||||
"description" : "",
|
||||
"versionName" : "1.9.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"modules": {},
|
||||
"distribute": {
|
||||
"android": {
|
||||
"permissions": [
|
||||
"modules" : {},
|
||||
"distribute" : {
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
@@ -35,36 +35,38 @@
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
"ios": {
|
||||
"dSYMs": false
|
||||
"ios" : {
|
||||
"dSYMs" : false
|
||||
},
|
||||
"sdkConfigs": {}
|
||||
"sdkConfigs" : {}
|
||||
}
|
||||
},
|
||||
"quickapp": {},
|
||||
"mp-weixin": {
|
||||
"appid": "wxccd7e2a0911b3397",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": false,
|
||||
"minified": true,
|
||||
"postcss": true
|
||||
"quickapp" : {},
|
||||
"mp-weixin" : {
|
||||
"appid" : "wxccd7e2a0911b3397",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"es6" : false,
|
||||
"minified" : true,
|
||||
"postcss" : true
|
||||
},
|
||||
"optimization": {
|
||||
"subPackages": true
|
||||
"optimization" : {
|
||||
"subPackages" : true
|
||||
},
|
||||
"usingComponents": true
|
||||
"usingComponents" : true
|
||||
},
|
||||
"vueVersion": "3",
|
||||
"h5": {
|
||||
"devServer": {
|
||||
"port": 9090,
|
||||
"https": false
|
||||
"vueVersion" : "3",
|
||||
"h5" : {
|
||||
"devServer" : {
|
||||
"port" : 9090,
|
||||
"https" : false
|
||||
},
|
||||
"title": "智链云枢管理系统",
|
||||
"router": {
|
||||
"mode": "hash",
|
||||
"base": "./"
|
||||
}
|
||||
"title" : "智链云枢管理系统",
|
||||
"router" : {
|
||||
"mode" : "hash",
|
||||
"base" : "./"
|
||||
}
|
||||
},
|
||||
"locale" : "zh-Hans",
|
||||
"fallbackLocale" : "zh-Hans"
|
||||
}
|
||||
|
||||
@@ -25,6 +25,36 @@
|
||||
"navigationBarTitleText": "工作台"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/inbound/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "仓库入库"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/filling/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "工厂装填"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/shipment/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发货出库"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/usage/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "现场使用"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/return/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "退回仓库"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/index",
|
||||
"style": {
|
||||
@@ -128,5 +158,15 @@
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "智链云枢管理系统",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
},
|
||||
"condition" : { //模式配置,仅开发期间生效
|
||||
"current": 0, //当前激活的模式(list 的索引项)
|
||||
"list": [
|
||||
{
|
||||
"name": "", //模式名称
|
||||
"path": "", //启动页面,必选
|
||||
"query": "" //启动参数,在页面的onLoad函数里面得到
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<view class="min-h-screen bg-gray-50">
|
||||
<scroll-view scroll-y class="h-screen">
|
||||
<view class="space-y-4 p-4 pb-24">
|
||||
<view>
|
||||
<text class="block text-xl font-bold text-gray-800">工厂装填</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
扫描空托盘,录入菌种、批次、规格和初始包数
|
||||
</text>
|
||||
</view>
|
||||
<TestBatchBanner />
|
||||
|
||||
<view v-if="!canPage" class="rounded-2xl bg-white p-6 text-center shadow-sm">
|
||||
<text class="text-sm text-gray-500">当前账号没有装填操作权限</text>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">扫描待装填托盘</text>
|
||||
<button
|
||||
class="text-xs text-gray-500"
|
||||
size="mini"
|
||||
:disabled="operationPending"
|
||||
@click="resetOperation"
|
||||
>
|
||||
清空
|
||||
</button>
|
||||
</view>
|
||||
<PalletScanInput
|
||||
:disabled="operationPending || optionLoading"
|
||||
:loading="scanLoading"
|
||||
@scan="handleFillingScan"
|
||||
@invalid="handleScanInvalid"
|
||||
@duplicate="handleScanDuplicate"
|
||||
/>
|
||||
<text class="mt-2 block text-xs text-gray-400">
|
||||
仅接受 EMPTY_IN_WAREHOUSE 状态托盘。
|
||||
</text>
|
||||
<text v-if="scanError" class="mt-2 block text-xs text-red-500">{{ scanError }}</text>
|
||||
|
||||
<view v-if="scannedPallet" class="mt-4 rounded-xl bg-green-50 p-3">
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="font-medium text-gray-700">
|
||||
{{ scannedPallet.palletCode || `#${scannedPallet.palletId}` }}
|
||||
</text>
|
||||
<text class="text-xs text-green-600">{{ scannedPallet.flowState }}</text>
|
||||
</view>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
仓库:{{ scannedPallet.warehouseName || `#${scannedPallet.warehouseId}` }} · 容量:{{ scannedPallet.capacityPackages }} 包
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<text class="mb-3 block font-bold text-gray-800">装填信息</text>
|
||||
<view class="space-y-3">
|
||||
<view>
|
||||
<text class="mb-2 block text-xs text-gray-500">菌种</text>
|
||||
<picker
|
||||
:range="strainOptions"
|
||||
range-key="name"
|
||||
:value="strainIndex"
|
||||
:disabled="operationPending || optionLoading || !scannedPallet"
|
||||
@change="handleStrainChange"
|
||||
>
|
||||
<view class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700">
|
||||
{{ selectedStrainName || "请选择启用菌种" }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view>
|
||||
<text class="mb-2 block text-xs text-gray-500">生产批次</text>
|
||||
<input
|
||||
v-model="form.batchNo"
|
||||
class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700"
|
||||
maxlength="64"
|
||||
placeholder="请输入生产批次"
|
||||
:disabled="operationPending || !scannedPallet"
|
||||
@input="markPayloadChanged"
|
||||
/>
|
||||
</view>
|
||||
<view>
|
||||
<text class="mb-2 block text-xs text-gray-500">包装规格</text>
|
||||
<input
|
||||
v-model="form.packageSpec"
|
||||
class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700"
|
||||
maxlength="64"
|
||||
placeholder="例如:500g/包"
|
||||
:disabled="operationPending || !scannedPallet"
|
||||
@input="markPayloadChanged"
|
||||
/>
|
||||
</view>
|
||||
<view>
|
||||
<text class="mb-2 block text-xs text-gray-500">初始包数</text>
|
||||
<input
|
||||
v-model="form.initialPackages"
|
||||
class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700"
|
||||
type="number"
|
||||
placeholder="请输入正整数"
|
||||
:disabled="operationPending || !scannedPallet"
|
||||
@input="markPayloadChanged"
|
||||
/>
|
||||
<text v-if="scannedPallet" class="mt-1 block text-xs text-gray-400">
|
||||
托盘容量 {{ scannedPallet.capacityPackages }} 包,最终容量由服务端校验
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="formError" class="mt-3 block text-xs text-red-500">{{ formError }}</text>
|
||||
<button
|
||||
class="mt-4 w-full rounded-xl bg-green-500 py-3 text-sm text-white active:opacity-80 disabled:bg-gray-300"
|
||||
:disabled="!canOperate || operationPending || scanLoading || !scannedPallet"
|
||||
@click="submitFilling"
|
||||
>
|
||||
{{ submitLoading ? "提交中" : "确认装填" }}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">最近装填记录</text>
|
||||
<button class="text-xs text-blue-500" size="mini" :disabled="loading" @click="loadFillingList">
|
||||
刷新
|
||||
</button>
|
||||
</view>
|
||||
<text v-if="listError" class="mb-3 block text-xs text-red-500">{{ listError }}</text>
|
||||
<view v-if="loading" class="py-6 text-center text-xs text-gray-400">加载中...</view>
|
||||
<view v-else-if="!fillingList.length" class="py-6 text-center text-xs text-gray-400">
|
||||
暂无装填记录
|
||||
</view>
|
||||
<view
|
||||
v-for="row in fillingList"
|
||||
v-else
|
||||
:key="row.fillingId"
|
||||
class="mb-3 rounded-xl border border-gray-100 p-3 last:mb-0"
|
||||
>
|
||||
<text class="block font-medium text-gray-700">{{ row.fillingNo || `#${row.fillingId}` }}</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
{{ row.strainNameSnapshot || row.strainCodeSnapshot }} · {{ row.batchNo }} · {{ row.initialPackages }} 包
|
||||
</text>
|
||||
<text class="mt-1 block text-xs text-gray-400">托盘 #{{ row.palletId }} · {{ row.operatedAt || row.createTime || "—" }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, reactive, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import PalletScanInput from "@/components/business/PalletScanInput.vue";
|
||||
import TestBatchBanner from "@/components/business/TestBatchBanner.vue";
|
||||
import {
|
||||
addFilling,
|
||||
getFillingStrainOptions,
|
||||
listFilling,
|
||||
scanFillingPallet,
|
||||
} from "@/api/business/filling";
|
||||
import { createIdempotencyKey } from "@/utils/palletScan";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const fillingList = ref([]);
|
||||
const strainOptions = ref([]);
|
||||
const scannedPallet = ref(null);
|
||||
const loading = ref(false);
|
||||
const optionLoading = ref(false);
|
||||
const scanLoading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const listError = ref("");
|
||||
const scanError = ref("");
|
||||
const formError = ref("");
|
||||
const idempotencyKey = ref("");
|
||||
const form = reactive({
|
||||
qrToken: "",
|
||||
warehouseId: null,
|
||||
strainId: null,
|
||||
batchNo: "",
|
||||
packageSpec: "",
|
||||
initialPackages: "",
|
||||
});
|
||||
let listGeneration = 0;
|
||||
let scanGeneration = 0;
|
||||
|
||||
const canPage = computed(() =>
|
||||
proxy.$auth.hasPermiOr([
|
||||
"business:filling:list",
|
||||
"business:filling:query",
|
||||
"business:filling:add",
|
||||
]),
|
||||
);
|
||||
const canList = computed(() => proxy.$auth.hasPermi("business:filling:list"));
|
||||
const canOperate = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:filling:query", "business:filling:add"]),
|
||||
);
|
||||
const operationPending = computed(() => submitLoading.value);
|
||||
const strainIndex = computed(() => {
|
||||
const index = strainOptions.value.findIndex((item) => item.id === form.strainId);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
const selectedStrainName = computed(
|
||||
() => strainOptions.value.find((item) => item.id === form.strainId)?.name || "",
|
||||
);
|
||||
|
||||
function errorMessage(error, fallback) {
|
||||
return error?.response?.data?.msg || error?.message || fallback;
|
||||
}
|
||||
|
||||
function loadFillingList() {
|
||||
if (!canList.value) return;
|
||||
const generation = ++listGeneration;
|
||||
loading.value = true;
|
||||
listError.value = "";
|
||||
listFilling({ pageNum: 1, pageSize: 20 })
|
||||
.then((response) => {
|
||||
if (generation !== listGeneration) return;
|
||||
fillingList.value = Array.isArray(response.rows) ? response.rows : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation !== listGeneration) return;
|
||||
fillingList.value = [];
|
||||
listError.value = errorMessage(error, "装填记录加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === listGeneration) loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function loadStrainOptions() {
|
||||
if (!proxy.$auth.hasPermi("business:filling:query")) return;
|
||||
optionLoading.value = true;
|
||||
getFillingStrainOptions()
|
||||
.then((response) => {
|
||||
strainOptions.value = Array.isArray(response.data) ? response.data : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
strainOptions.value = [];
|
||||
formError.value = errorMessage(error, "菌种选项加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
optionLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetOperation() {
|
||||
scanGeneration += 1;
|
||||
scannedPallet.value = null;
|
||||
idempotencyKey.value = "";
|
||||
form.qrToken = "";
|
||||
form.warehouseId = null;
|
||||
form.strainId = null;
|
||||
form.batchNo = "";
|
||||
form.packageSpec = "";
|
||||
form.initialPackages = "";
|
||||
scanError.value = "";
|
||||
formError.value = "";
|
||||
scanLoading.value = false;
|
||||
}
|
||||
|
||||
function handleFillingScan(qrToken) {
|
||||
if (operationPending.value || scanLoading.value || optionLoading.value) return;
|
||||
resetOperation();
|
||||
const generation = ++scanGeneration;
|
||||
scanLoading.value = true;
|
||||
scanError.value = "";
|
||||
scanFillingPallet(qrToken)
|
||||
.then((response) => {
|
||||
if (generation !== scanGeneration) return;
|
||||
const pallet = response.data;
|
||||
if (pallet?.flowState !== "EMPTY_IN_WAREHOUSE") {
|
||||
scanError.value = "该托盘当前不可装填";
|
||||
return;
|
||||
}
|
||||
scannedPallet.value = {
|
||||
palletId: pallet.palletId,
|
||||
palletCode: pallet.palletCode || `#${pallet.palletId}`,
|
||||
warehouseId: pallet.warehouseId,
|
||||
warehouseName: pallet.warehouseName,
|
||||
flowState: pallet.flowState,
|
||||
capacityPackages: pallet.capacityPackages,
|
||||
};
|
||||
form.qrToken = qrToken;
|
||||
form.warehouseId = pallet.warehouseId;
|
||||
loadStrainOptions();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation === scanGeneration) {
|
||||
scanError.value = errorMessage(error, "托盘扫码校验失败,请检查后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === scanGeneration) scanLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleScanInvalid(message) {
|
||||
resetOperation();
|
||||
scanError.value = message;
|
||||
}
|
||||
|
||||
function handleScanDuplicate() {
|
||||
proxy.$modal.msg("扫码过快,请勿重复扫描");
|
||||
}
|
||||
|
||||
function handleStrainChange(event) {
|
||||
const option = strainOptions.value[Number(event.detail.value)];
|
||||
form.strainId = option?.id || null;
|
||||
markPayloadChanged();
|
||||
}
|
||||
|
||||
function markPayloadChanged() {
|
||||
if (!submitLoading.value) idempotencyKey.value = "";
|
||||
}
|
||||
|
||||
function ensureIdempotencyKey() {
|
||||
if (!idempotencyKey.value) idempotencyKey.value = createIdempotencyKey();
|
||||
return idempotencyKey.value;
|
||||
}
|
||||
|
||||
function submitFilling() {
|
||||
if (!canOperate.value || operationPending.value || scanLoading.value || optionLoading.value) return;
|
||||
const initialPackages = Number(form.initialPackages);
|
||||
if (!scannedPallet.value || !form.qrToken || !form.warehouseId) {
|
||||
formError.value = "请先扫描待装填托盘";
|
||||
return;
|
||||
}
|
||||
if (!form.strainId || !form.batchNo.trim() || !form.packageSpec.trim()) {
|
||||
formError.value = "请完整填写菌种、批次和包装规格";
|
||||
return;
|
||||
}
|
||||
if (!Number.isInteger(initialPackages) || initialPackages <= 0) {
|
||||
formError.value = "初始包数必须为正整数";
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
formError.value = "";
|
||||
addFilling({
|
||||
qrToken: form.qrToken,
|
||||
warehouseId: form.warehouseId,
|
||||
strainId: form.strainId,
|
||||
batchNo: form.batchNo.trim(),
|
||||
packageSpec: form.packageSpec.trim(),
|
||||
initialPackages,
|
||||
idempotencyKey: ensureIdempotencyKey(),
|
||||
})
|
||||
.then((response) => {
|
||||
idempotencyKey.value = "";
|
||||
proxy.$modal.msgSuccess(response.msg || "托盘装填成功");
|
||||
resetOperation();
|
||||
loadFillingList();
|
||||
})
|
||||
.catch((error) => {
|
||||
formError.value = `${errorMessage(error, "装填提交失败")};直接重试将复用本次幂等键,修改输入后会生成新键`;
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadFillingList();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,486 @@
|
||||
<template>
|
||||
<view class="min-h-screen bg-gray-50">
|
||||
<scroll-view scroll-y class="h-screen">
|
||||
<view class="space-y-4 p-4 pb-24">
|
||||
<view class="flex items-center justify-between">
|
||||
<view>
|
||||
<text class="block text-xl font-bold text-gray-800">仓库入库</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
扫描尚未首次入库的空托盘,保存草稿后确认
|
||||
</text>
|
||||
</view>
|
||||
<button
|
||||
v-if="canDraft"
|
||||
class="rounded-lg bg-blue-500 px-3 py-2 text-xs text-white active:opacity-80"
|
||||
size="mini"
|
||||
:disabled="operationPending"
|
||||
@click="startNewDraft"
|
||||
>
|
||||
新建草稿
|
||||
</button>
|
||||
</view>
|
||||
<TestBatchBanner />
|
||||
|
||||
<view v-if="!canPage" class="rounded-2xl bg-white p-6 text-center shadow-sm">
|
||||
<text class="text-sm text-gray-500">当前账号没有入库操作权限</text>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<view v-if="editorVisible" class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-4 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">
|
||||
{{ editing ? "编辑入库草稿" : "新建入库草稿" }}
|
||||
</text>
|
||||
<text class="text-xs text-gray-400">托盘数量 {{ scannedPallets.length }}</text>
|
||||
</view>
|
||||
|
||||
<view class="mb-3">
|
||||
<text class="mb-2 block text-xs text-gray-500">入库仓库</text>
|
||||
<picker
|
||||
:range="warehouseOptions"
|
||||
range-key="name"
|
||||
:value="warehouseIndex"
|
||||
:disabled="optionLoading || detailLoading || operationPending"
|
||||
@change="handleWarehouseChange"
|
||||
>
|
||||
<view class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700">
|
||||
{{ selectedWarehouseName || "请选择授权仓库" }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="mb-3">
|
||||
<text class="mb-2 block text-xs text-gray-500">连续扫码</text>
|
||||
<PalletScanInput
|
||||
:disabled="!form.warehouseId || detailLoading || operationPending"
|
||||
:loading="scanLoading"
|
||||
@scan="handleInboundScan"
|
||||
@invalid="handleScanInvalid"
|
||||
@duplicate="handleScanDuplicate"
|
||||
/>
|
||||
<text class="mt-2 block text-xs text-gray-400">
|
||||
仅接受 UNSTOCKED 托盘;同一托盘不会重复加入。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<text v-if="scanError" class="mb-3 block text-xs text-red-500">{{ scanError }}</text>
|
||||
<text v-if="formError" class="mb-3 block text-xs text-red-500">{{ formError }}</text>
|
||||
|
||||
<view v-if="scannedPallets.length" class="mb-4 space-y-2">
|
||||
<view
|
||||
v-for="pallet in scannedPallets"
|
||||
:key="pallet.palletId"
|
||||
class="flex items-center justify-between rounded-xl bg-blue-50 px-3 py-2"
|
||||
>
|
||||
<view>
|
||||
<text class="block text-sm font-medium text-gray-700">
|
||||
{{ pallet.palletCode || `#${pallet.palletId}` }}
|
||||
</text>
|
||||
<text class="block text-xs text-gray-500">{{ pallet.flowState }}</text>
|
||||
</view>
|
||||
<button
|
||||
class="text-xs text-red-500"
|
||||
size="mini"
|
||||
:disabled="operationPending || detailLoading"
|
||||
@click="removeScannedPallet(pallet.palletId)"
|
||||
>
|
||||
移除
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex gap-3">
|
||||
<button
|
||||
class="flex-1 rounded-xl bg-blue-500 py-3 text-sm text-white active:opacity-80 disabled:bg-gray-300"
|
||||
:disabled="!canSaveDraft || operationPending || scanLoading || detailLoading"
|
||||
@click="saveDraft"
|
||||
>
|
||||
{{ submitLoading ? "保存中" : editing ? "保存修改" : "保存草稿" }}
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 rounded-xl bg-gray-100 py-3 text-sm text-gray-600 active:opacity-80"
|
||||
:disabled="operationPending"
|
||||
@click="cancelEditor"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">入库单</text>
|
||||
<button
|
||||
class="text-xs text-blue-500"
|
||||
size="mini"
|
||||
:disabled="loading || operationPending"
|
||||
@click="loadInboundList"
|
||||
>
|
||||
刷新
|
||||
</button>
|
||||
</view>
|
||||
<text v-if="listError" class="mb-3 block text-xs text-red-500">{{ listError }}</text>
|
||||
<text v-if="confirmError" class="mb-3 block text-xs text-red-500">{{ confirmError }}</text>
|
||||
<view v-if="loading" class="py-6 text-center text-xs text-gray-400">加载中...</view>
|
||||
<view v-else-if="!inboundList.length" class="py-6 text-center text-xs text-gray-400">
|
||||
暂无入库单
|
||||
</view>
|
||||
<view
|
||||
v-for="row in inboundList"
|
||||
v-else
|
||||
:key="row.inboundId"
|
||||
class="mb-3 rounded-xl border border-gray-100 p-3 last:mb-0"
|
||||
>
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="font-medium text-gray-700">{{ row.inboundNo || `#${row.inboundId}` }}</text>
|
||||
<text :class="row.status === 'DRAFT' ? 'text-orange-500' : 'text-green-500'" class="text-xs">
|
||||
{{ row.status === "DRAFT" ? "草稿" : "已确认" }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
仓库:{{ warehouseName(row.warehouseId) }} · 托盘:{{ row.items?.length || 0 }} 个
|
||||
</text>
|
||||
<view v-if="row.status === 'DRAFT'" class="mt-3 flex gap-2">
|
||||
<button
|
||||
v-if="canEdit"
|
||||
class="flex-1 rounded-lg bg-blue-50 py-2 text-xs text-blue-600"
|
||||
size="mini"
|
||||
:disabled="operationPending"
|
||||
@click="startEditDraft(row)"
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
v-if="canConfirm"
|
||||
class="flex-1 rounded-lg bg-green-50 py-2 text-xs text-green-600"
|
||||
size="mini"
|
||||
:disabled="operationPending"
|
||||
@click="confirmDraft(row)"
|
||||
>
|
||||
{{ confirmLoading ? "确认中" : "确认" }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, reactive, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import PalletScanInput from "@/components/business/PalletScanInput.vue";
|
||||
import TestBatchBanner from "@/components/business/TestBatchBanner.vue";
|
||||
import {
|
||||
addInbound,
|
||||
confirmInbound,
|
||||
getInbound,
|
||||
getInboundWarehouseOptions,
|
||||
listInbound,
|
||||
scanInboundPallet,
|
||||
updateInbound,
|
||||
} from "@/api/business/inbound";
|
||||
import { createIdempotencyKey } from "@/utils/palletScan";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const inboundList = ref([]);
|
||||
const warehouseOptions = ref([]);
|
||||
const scannedPallets = ref([]);
|
||||
const editorVisible = ref(false);
|
||||
const editing = ref(false);
|
||||
const loading = ref(false);
|
||||
const optionLoading = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const scanLoading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const confirmLoading = ref(false);
|
||||
const listError = ref("");
|
||||
const formError = ref("");
|
||||
const scanError = ref("");
|
||||
const confirmError = ref("");
|
||||
const form = reactive({ inboundId: null, warehouseId: null });
|
||||
const confirmState = reactive({ inboundId: null, key: "" });
|
||||
let listGeneration = 0;
|
||||
let scanGeneration = 0;
|
||||
|
||||
const canPage = computed(() =>
|
||||
proxy.$auth.hasPermiOr([
|
||||
"business:inbound:list",
|
||||
"business:inbound:query",
|
||||
"business:inbound:add",
|
||||
"business:inbound:edit",
|
||||
"business:inbound:confirm",
|
||||
]),
|
||||
);
|
||||
const canList = computed(() => proxy.$auth.hasPermi("business:inbound:list"));
|
||||
const canDraft = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:inbound:query", "business:inbound:add"]),
|
||||
);
|
||||
const canEdit = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:inbound:query", "business:inbound:edit"]),
|
||||
);
|
||||
const canConfirm = computed(() =>
|
||||
proxy.$auth.hasPermi("business:inbound:confirm"),
|
||||
);
|
||||
const canSaveDraft = computed(() => (editing.value ? canEdit.value : canDraft.value));
|
||||
const operationPending = computed(
|
||||
() =>
|
||||
submitLoading.value ||
|
||||
confirmLoading.value ||
|
||||
detailLoading.value,
|
||||
);
|
||||
const warehouseIndex = computed(() => {
|
||||
const index = warehouseOptions.value.findIndex(
|
||||
(item) => item.id === form.warehouseId,
|
||||
);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
const selectedWarehouseName = computed(
|
||||
() => warehouseOptions.value.find((item) => item.id === form.warehouseId)?.name || "",
|
||||
);
|
||||
|
||||
function errorMessage(error, fallback) {
|
||||
return error?.response?.data?.msg || error?.message || fallback;
|
||||
}
|
||||
|
||||
function warehouseName(id) {
|
||||
return warehouseOptions.value.find((item) => item.id === id)?.name || `#${id || "-"}`;
|
||||
}
|
||||
|
||||
function resetEditor() {
|
||||
scanGeneration += 1;
|
||||
editorVisible.value = false;
|
||||
editing.value = false;
|
||||
form.inboundId = null;
|
||||
form.warehouseId = null;
|
||||
scannedPallets.value = [];
|
||||
detailLoading.value = false;
|
||||
scanLoading.value = false;
|
||||
formError.value = "";
|
||||
scanError.value = "";
|
||||
}
|
||||
|
||||
function loadInboundList() {
|
||||
if (!canList.value) return;
|
||||
const generation = ++listGeneration;
|
||||
loading.value = true;
|
||||
listError.value = "";
|
||||
listInbound({ pageNum: 1, pageSize: 20 })
|
||||
.then((response) => {
|
||||
if (generation !== listGeneration) return;
|
||||
inboundList.value = Array.isArray(response.rows) ? response.rows : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation !== listGeneration) return;
|
||||
inboundList.value = [];
|
||||
listError.value = errorMessage(error, "入库单列表加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === listGeneration) loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function loadWarehouseOptions() {
|
||||
if (!proxy.$auth.hasPermi("business:inbound:query")) return;
|
||||
optionLoading.value = true;
|
||||
getInboundWarehouseOptions()
|
||||
.then((response) => {
|
||||
warehouseOptions.value = Array.isArray(response.data) ? response.data : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
warehouseOptions.value = [];
|
||||
formError.value = errorMessage(error, "仓库选项加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
optionLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function startNewDraft() {
|
||||
if (!canDraft.value || operationPending.value) return;
|
||||
resetEditor();
|
||||
editorVisible.value = true;
|
||||
loadWarehouseOptions();
|
||||
}
|
||||
|
||||
function startEditDraft(row) {
|
||||
if (!canEdit.value || row.status !== "DRAFT" || operationPending.value) return;
|
||||
resetEditor();
|
||||
editorVisible.value = true;
|
||||
editing.value = true;
|
||||
form.inboundId = row.inboundId;
|
||||
loadWarehouseOptions();
|
||||
detailLoading.value = true;
|
||||
getInbound(row.inboundId)
|
||||
.then((response) => {
|
||||
const detail = response.data;
|
||||
if (!detail || detail.status !== "DRAFT") {
|
||||
formError.value = "该入库单已不是草稿状态,不能编辑";
|
||||
return;
|
||||
}
|
||||
form.warehouseId = detail.warehouseId;
|
||||
scannedPallets.value = (Array.isArray(detail.items) ? detail.items : []).map(
|
||||
(item) => ({
|
||||
palletId: item.palletId,
|
||||
palletCode: item.palletCode || `#${item.palletId}`,
|
||||
flowState: item.flowState || "UNSTOCKED",
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
formError.value = errorMessage(error, "入库单详情加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
detailLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function cancelEditor() {
|
||||
if (operationPending.value) return;
|
||||
resetEditor();
|
||||
}
|
||||
|
||||
function handleWarehouseChange(event) {
|
||||
const option = warehouseOptions.value[Number(event.detail.value)];
|
||||
if (!option || option.id === form.warehouseId) return;
|
||||
form.warehouseId = option.id;
|
||||
scannedPallets.value = [];
|
||||
scanError.value = "";
|
||||
scanGeneration += 1;
|
||||
}
|
||||
|
||||
function handleInboundScan(qrToken) {
|
||||
if (!form.warehouseId || operationPending.value || scanLoading.value) {
|
||||
scanError.value = "请先选择入库仓库";
|
||||
return;
|
||||
}
|
||||
const generation = ++scanGeneration;
|
||||
scanLoading.value = true;
|
||||
scanError.value = "";
|
||||
scanInboundPallet(qrToken, { warehouseId: form.warehouseId })
|
||||
.then((response) => {
|
||||
if (generation !== scanGeneration) return;
|
||||
const pallet = response.data;
|
||||
if (pallet?.flowState !== "UNSTOCKED") {
|
||||
scanError.value = "该托盘不是未入库状态,不能加入入库草稿";
|
||||
return;
|
||||
}
|
||||
if (scannedPallets.value.some((item) => item.palletId === pallet.palletId)) {
|
||||
proxy.$modal.msg("该托盘已在当前草稿中");
|
||||
return;
|
||||
}
|
||||
scannedPallets.value.push({
|
||||
palletId: pallet.palletId,
|
||||
palletCode: pallet.palletCode || `#${pallet.palletId}`,
|
||||
flowState: pallet.flowState,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation === scanGeneration) {
|
||||
scanError.value = errorMessage(error, "托盘扫码校验失败,请检查后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === scanGeneration) scanLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleScanInvalid(message) {
|
||||
scanError.value = message;
|
||||
}
|
||||
|
||||
function handleScanDuplicate() {
|
||||
proxy.$modal.msg("扫码过快,请勿重复扫描");
|
||||
}
|
||||
|
||||
function removeScannedPallet(palletId) {
|
||||
if (operationPending.value) return;
|
||||
scannedPallets.value = scannedPallets.value.filter(
|
||||
(item) => item.palletId !== palletId,
|
||||
);
|
||||
}
|
||||
|
||||
function saveDraft() {
|
||||
if (!canSaveDraft.value || operationPending.value || scanLoading.value) return;
|
||||
if (!form.warehouseId) {
|
||||
formError.value = "请选择入库仓库";
|
||||
return;
|
||||
}
|
||||
if (!scannedPallets.value.length) {
|
||||
formError.value = "请至少扫描一个托盘";
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
formError.value = "";
|
||||
const payload = {
|
||||
warehouseId: form.warehouseId,
|
||||
palletIds: scannedPallets.value.map((item) => item.palletId),
|
||||
};
|
||||
const submitRequest = editing.value
|
||||
? updateInbound({ ...payload, inboundId: form.inboundId })
|
||||
: addInbound(payload);
|
||||
submitRequest
|
||||
.then((response) => {
|
||||
proxy.$modal.msgSuccess(response.msg || "入库草稿保存成功");
|
||||
resetEditor();
|
||||
loadInboundList();
|
||||
})
|
||||
.catch((error) => {
|
||||
formError.value = errorMessage(error, "入库草稿保存失败,请根据提示修正后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function askConfirm(content) {
|
||||
return new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title: "系统提示",
|
||||
content,
|
||||
cancelText: "取消",
|
||||
confirmText: "确定",
|
||||
success: (result) => resolve(Boolean(result.confirm)),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function ensureConfirmIdempotencyKey(inboundId) {
|
||||
if (confirmState.inboundId !== inboundId) {
|
||||
confirmState.inboundId = inboundId;
|
||||
confirmState.key = "";
|
||||
}
|
||||
if (!confirmState.key) confirmState.key = createIdempotencyKey();
|
||||
return confirmState.key;
|
||||
}
|
||||
|
||||
function clearConfirmIdempotency() {
|
||||
confirmState.inboundId = null;
|
||||
confirmState.key = "";
|
||||
}
|
||||
|
||||
async function confirmDraft(row) {
|
||||
if (!canConfirm.value || row.status !== "DRAFT" || operationPending.value) return;
|
||||
if (!(await askConfirm(`确认入库单“${row.inboundNo || `#${row.inboundId}`}”?`))) return;
|
||||
confirmLoading.value = true;
|
||||
confirmError.value = "";
|
||||
try {
|
||||
await confirmInbound(row.inboundId, {
|
||||
idempotencyKey: ensureConfirmIdempotencyKey(row.inboundId),
|
||||
});
|
||||
clearConfirmIdempotency();
|
||||
proxy.$modal.msgSuccess("入库确认成功");
|
||||
loadInboundList();
|
||||
} catch (error) {
|
||||
confirmError.value = `${errorMessage(error, "入库确认失败")};再次确认将复用本次幂等键`;
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadInboundList();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,560 @@
|
||||
<template>
|
||||
<view class="min-h-screen bg-gray-50">
|
||||
<scroll-view scroll-y class="h-screen">
|
||||
<view class="space-y-4 p-4 pb-24">
|
||||
<view>
|
||||
<text class="block text-xl font-bold text-gray-800">退回仓库</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
客户现场发起退回申请,仓库逐托扫描后确认收货
|
||||
</text>
|
||||
</view>
|
||||
<TestBatchBanner />
|
||||
|
||||
<view v-if="!canPage" class="rounded-2xl bg-white p-6 text-center shadow-sm">
|
||||
<text class="text-sm text-gray-500">当前账号没有退回操作权限</text>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<view v-if="canRequest" class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">发起退回申请</text>
|
||||
<button class="text-xs text-gray-500" size="mini" :disabled="requestPending" @click="resetRequestOperation">
|
||||
清空
|
||||
</button>
|
||||
</view>
|
||||
<view class="mb-3">
|
||||
<text class="mb-2 block text-xs text-gray-500">目标仓库</text>
|
||||
<picker
|
||||
:range="warehouseOptions"
|
||||
range-key="name"
|
||||
:value="warehouseIndex"
|
||||
:disabled="optionLoading || requestPending"
|
||||
@change="handleWarehouseChange"
|
||||
>
|
||||
<view class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700">
|
||||
{{ selectedWarehouseName || "请选择目标仓库" }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<PalletScanInput
|
||||
:disabled="!requestForm.targetWarehouseId || requestPending"
|
||||
:loading="scanLoading.request"
|
||||
@scan="handleRequestScan"
|
||||
@invalid="handleRequestScanInvalid"
|
||||
@duplicate="handleScanDuplicate"
|
||||
/>
|
||||
<text class="mt-2 block text-xs text-gray-400">
|
||||
同一申请只能包含同一客户现场的托盘;扫码成功后托盘由服务端冻结。
|
||||
</text>
|
||||
<text v-if="requestError" class="mt-2 block text-xs text-red-500">{{ requestError }}</text>
|
||||
<view v-if="requestScannedPallets.length" class="mt-3 space-y-2">
|
||||
<view
|
||||
v-for="pallet in requestScannedPallets"
|
||||
:key="pallet.palletId"
|
||||
class="flex items-center justify-between rounded-xl bg-blue-50 px-3 py-2"
|
||||
>
|
||||
<view>
|
||||
<text class="block text-sm text-gray-700">{{ pallet.palletCode || `#${pallet.palletId}` }}</text>
|
||||
<text class="block text-xs text-gray-500">现场 #{{ pallet.siteId }} · 剩余 {{ pallet.remainingPackages }} 包</text>
|
||||
</view>
|
||||
<button class="text-xs text-red-500" size="mini" :disabled="requestPending" @click="removeRequestPallet(pallet.palletId)">
|
||||
移除
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="requestForm.sourceSiteId" class="mt-3 block text-xs text-gray-500">
|
||||
来源现场:#{{ requestForm.sourceSiteId }} · {{ requestScannedPallets.length }} 个托盘
|
||||
</text>
|
||||
<button
|
||||
class="mt-4 w-full rounded-xl bg-blue-500 py-3 text-sm text-white active:opacity-80 disabled:bg-gray-300"
|
||||
:disabled="!requestScannedPallets.length || requestPending || scanLoading.request"
|
||||
@click="submitReturnRequest"
|
||||
>
|
||||
{{ requestSubmitLoading ? "申请中" : "提交退回申请" }}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view v-if="canConfirm" class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">仓库确认退回</text>
|
||||
<text class="text-xs text-gray-400">必须完整扫描单据托盘</text>
|
||||
</view>
|
||||
<picker
|
||||
:range="requestedReturns"
|
||||
range-key="returnNo"
|
||||
:value="requestedReturnIndex"
|
||||
:disabled="confirmPending || detailLoading"
|
||||
@change="handleConfirmSelection"
|
||||
>
|
||||
<view class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700">
|
||||
{{ selectedReturnLabel || "请选择待确认退回单" }}
|
||||
</view>
|
||||
</picker>
|
||||
<text v-if="confirmError" class="mt-2 block text-xs text-red-500">{{ confirmError }}</text>
|
||||
<view v-if="confirmExpectedRows.length" class="mt-3 rounded-xl bg-orange-50 p-3">
|
||||
<text class="block text-xs text-gray-500">
|
||||
预期托盘 {{ confirmExpectedRows.length }} 个,已扫描 {{ confirmScannedPallets.length }} 个
|
||||
</text>
|
||||
<view class="mt-2 space-y-1">
|
||||
<text v-for="row in confirmExpectedRows" :key="row.palletId" class="block text-xs text-gray-600">
|
||||
{{ row.palletCode || `#${row.palletId}` }}
|
||||
<text v-if="isConfirmPalletScanned(row.palletId)" class="text-green-600"> · 已扫描</text>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<PalletScanInput
|
||||
class="mt-3"
|
||||
:disabled="!confirmForm.returnId || !confirmExpectedRows.length || confirmPending || detailLoading"
|
||||
:loading="scanLoading.confirm"
|
||||
@scan="handleConfirmScan"
|
||||
@invalid="handleConfirmScanInvalid"
|
||||
@duplicate="handleScanDuplicate"
|
||||
/>
|
||||
<button
|
||||
class="mt-4 w-full rounded-xl bg-orange-500 py-3 text-sm text-white active:opacity-80 disabled:bg-gray-300"
|
||||
:disabled="!confirmForm.returnId || !confirmExpectedRows.length || confirmPending || scanLoading.confirm || detailLoading"
|
||||
@click="submitReturnConfirm"
|
||||
>
|
||||
{{ confirmSubmitLoading ? "确认中" : "确认退回收货" }}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">退回单</text>
|
||||
<button class="text-xs text-blue-500" size="mini" :disabled="loading" @click="loadReturnList">
|
||||
刷新
|
||||
</button>
|
||||
</view>
|
||||
<text v-if="listError" class="mb-3 block text-xs text-red-500">{{ listError }}</text>
|
||||
<view v-if="loading" class="py-6 text-center text-xs text-gray-400">加载中...</view>
|
||||
<view v-else-if="!returnList.length" class="py-6 text-center text-xs text-gray-400">
|
||||
暂无退回单
|
||||
</view>
|
||||
<view
|
||||
v-for="row in returnList"
|
||||
v-else
|
||||
:key="row.returnId"
|
||||
class="mb-3 rounded-xl border border-gray-100 p-3 last:mb-0"
|
||||
>
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="font-medium text-gray-700">{{ row.returnNo || `#${row.returnId}` }}</text>
|
||||
<text :class="row.status === 'REQUESTED' ? 'text-orange-500' : 'text-green-500'" class="text-xs">
|
||||
{{ row.status === "REQUESTED" ? "待确认" : "已确认" }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
现场 #{{ row.sourceSiteId }} → {{ warehouseName(row.targetWarehouseId) }} · 托盘 {{ row.items?.length || 0 }} 个
|
||||
</text>
|
||||
<button
|
||||
v-if="row.status === 'REQUESTED' && canConfirm"
|
||||
class="mt-3 w-full rounded-lg bg-orange-50 py-2 text-xs text-orange-600"
|
||||
size="mini"
|
||||
:disabled="confirmPending"
|
||||
@click="selectReturnRow(row)"
|
||||
>
|
||||
选择并确认
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, reactive, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import PalletScanInput from "@/components/business/PalletScanInput.vue";
|
||||
import TestBatchBanner from "@/components/business/TestBatchBanner.vue";
|
||||
import {
|
||||
confirmReturn,
|
||||
getReturn,
|
||||
getReturnWarehouseOptions,
|
||||
listReturn,
|
||||
requestReturn,
|
||||
scanReturnPallet,
|
||||
} from "@/api/business/return";
|
||||
import { createIdempotencyKey } from "@/utils/palletScan";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const returnList = ref([]);
|
||||
const warehouseOptions = ref([]);
|
||||
const requestScannedPallets = ref([]);
|
||||
const confirmExpectedRows = ref([]);
|
||||
const confirmExpectedPalletIds = ref([]);
|
||||
const confirmScannedPallets = ref([]);
|
||||
const loading = ref(false);
|
||||
const optionLoading = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const listError = ref("");
|
||||
const requestError = ref("");
|
||||
const confirmError = ref("");
|
||||
const scanLoading = reactive({ request: false, confirm: false });
|
||||
const requestSubmitLoading = ref(false);
|
||||
const confirmSubmitLoading = ref(false);
|
||||
const requestIdempotencyKey = ref("");
|
||||
const confirmIdempotencyKey = ref("");
|
||||
const requestForm = reactive({ sourceSiteId: null, targetWarehouseId: null });
|
||||
const confirmForm = reactive({ returnId: null });
|
||||
let listGeneration = 0;
|
||||
let requestScanGeneration = 0;
|
||||
let confirmScanGeneration = 0;
|
||||
let detailGeneration = 0;
|
||||
let requestSession = 0;
|
||||
let confirmSession = 0;
|
||||
|
||||
const canPage = computed(() =>
|
||||
proxy.$auth.hasPermiOr([
|
||||
"business:return:list",
|
||||
"business:return:query",
|
||||
"business:return:request",
|
||||
"business:return:confirm",
|
||||
]),
|
||||
);
|
||||
const canList = computed(() => proxy.$auth.hasPermi("business:return:list"));
|
||||
const canRequest = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:return:query", "business:return:request"]),
|
||||
);
|
||||
const canConfirm = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:return:query", "business:return:confirm"]),
|
||||
);
|
||||
const requestPending = computed(() => requestSubmitLoading.value);
|
||||
const confirmPending = computed(() => confirmSubmitLoading.value);
|
||||
const warehouseIndex = computed(() => {
|
||||
const index = warehouseOptions.value.findIndex(
|
||||
(item) => item.id === requestForm.targetWarehouseId,
|
||||
);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
const selectedWarehouseName = computed(
|
||||
() => warehouseOptions.value.find((item) => item.id === requestForm.targetWarehouseId)?.name || "",
|
||||
);
|
||||
const requestedReturns = computed(() =>
|
||||
returnList.value.filter((item) => item.status === "REQUESTED"),
|
||||
);
|
||||
const requestedReturnIndex = computed(() => {
|
||||
const index = requestedReturns.value.findIndex(
|
||||
(item) => item.returnId === confirmForm.returnId,
|
||||
);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
const selectedReturnLabel = computed(() => {
|
||||
const item = requestedReturns.value.find((row) => row.returnId === confirmForm.returnId);
|
||||
return item ? `${item.returnNo || `#${item.returnId}`} · 现场 #${item.sourceSiteId}` : "";
|
||||
});
|
||||
|
||||
function errorMessage(error, fallback) {
|
||||
return error?.response?.data?.msg || error?.message || fallback;
|
||||
}
|
||||
|
||||
function warehouseName(id) {
|
||||
return warehouseOptions.value.find((item) => item.id === id)?.name || `仓库 #${id || "-"}`;
|
||||
}
|
||||
|
||||
function loadReturnList() {
|
||||
if (!canList.value) return;
|
||||
const generation = ++listGeneration;
|
||||
loading.value = true;
|
||||
listError.value = "";
|
||||
listReturn({ pageNum: 1, pageSize: 30 })
|
||||
.then((response) => {
|
||||
if (generation !== listGeneration) return;
|
||||
returnList.value = Array.isArray(response.rows) ? response.rows : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation !== listGeneration) return;
|
||||
returnList.value = [];
|
||||
listError.value = errorMessage(error, "退回单列表加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === listGeneration) loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function loadWarehouseOptions() {
|
||||
if (!proxy.$auth.hasPermi("business:return:query")) return;
|
||||
optionLoading.value = true;
|
||||
getReturnWarehouseOptions()
|
||||
.then((response) => {
|
||||
warehouseOptions.value = Array.isArray(response.data) ? response.data : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
warehouseOptions.value = [];
|
||||
requestError.value = errorMessage(error, "目标仓库选项加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
optionLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetRequestOperation() {
|
||||
requestSession += 1;
|
||||
requestScanGeneration += 1;
|
||||
requestForm.sourceSiteId = null;
|
||||
requestForm.targetWarehouseId = null;
|
||||
requestScannedPallets.value = [];
|
||||
requestIdempotencyKey.value = "";
|
||||
requestError.value = "";
|
||||
scanLoading.request = false;
|
||||
}
|
||||
|
||||
function handleWarehouseChange(event) {
|
||||
const option = warehouseOptions.value[Number(event.detail.value)];
|
||||
if (!option || option.id === requestForm.targetWarehouseId) return;
|
||||
requestForm.targetWarehouseId = option.id;
|
||||
requestForm.sourceSiteId = null;
|
||||
requestScannedPallets.value = [];
|
||||
requestIdempotencyKey.value = "";
|
||||
requestError.value = "";
|
||||
requestScanGeneration += 1;
|
||||
}
|
||||
|
||||
function handleRequestScan(qrToken) {
|
||||
if (!requestForm.targetWarehouseId || requestPending.value || scanLoading.request) {
|
||||
requestError.value = "请先选择目标仓库";
|
||||
return;
|
||||
}
|
||||
const session = requestSession;
|
||||
const generation = ++requestScanGeneration;
|
||||
scanLoading.request = true;
|
||||
requestError.value = "";
|
||||
scanReturnPallet(qrToken, { mode: "request" })
|
||||
.then((response) => {
|
||||
if (session !== requestSession || generation !== requestScanGeneration) return;
|
||||
const pallet = response.data;
|
||||
if (!pallet?.siteId) {
|
||||
requestError.value = "该托盘不在客户现场,不能发起退回";
|
||||
return;
|
||||
}
|
||||
if (requestForm.sourceSiteId && pallet.siteId !== requestForm.sourceSiteId) {
|
||||
requestError.value = "同一退回申请只能包含同一客户现场的托盘";
|
||||
return;
|
||||
}
|
||||
if (requestScannedPallets.value.some((item) => item.palletId === pallet.palletId)) {
|
||||
proxy.$modal.msg("该托盘已在退回申请中");
|
||||
return;
|
||||
}
|
||||
requestForm.sourceSiteId = requestForm.sourceSiteId || pallet.siteId;
|
||||
requestScannedPallets.value.push({
|
||||
palletId: pallet.palletId,
|
||||
palletCode: pallet.palletCode || `#${pallet.palletId}`,
|
||||
siteId: pallet.siteId,
|
||||
remainingPackages: pallet.remainingPackages,
|
||||
});
|
||||
requestIdempotencyKey.value = "";
|
||||
})
|
||||
.catch((error) => {
|
||||
if (session === requestSession && generation === requestScanGeneration) {
|
||||
requestError.value = errorMessage(error, "退回托盘扫码校验失败,请检查后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (session === requestSession && generation === requestScanGeneration) {
|
||||
scanLoading.request = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleRequestScanInvalid(message) {
|
||||
requestError.value = message;
|
||||
}
|
||||
|
||||
function removeRequestPallet(palletId) {
|
||||
if (requestPending.value) return;
|
||||
requestScannedPallets.value = requestScannedPallets.value.filter(
|
||||
(item) => item.palletId !== palletId,
|
||||
);
|
||||
if (!requestScannedPallets.value.length) requestForm.sourceSiteId = null;
|
||||
requestIdempotencyKey.value = "";
|
||||
}
|
||||
|
||||
function ensureRequestIdempotencyKey() {
|
||||
if (!requestIdempotencyKey.value) requestIdempotencyKey.value = createIdempotencyKey();
|
||||
return requestIdempotencyKey.value;
|
||||
}
|
||||
|
||||
async function submitReturnRequest() {
|
||||
if (requestPending.value || scanLoading.request || !requestScannedPallets.value.length) return;
|
||||
if (!requestForm.sourceSiteId || !requestForm.targetWarehouseId) {
|
||||
requestError.value = "请选择目标仓库并扫描退回托盘";
|
||||
return;
|
||||
}
|
||||
requestSubmitLoading.value = true;
|
||||
requestError.value = "";
|
||||
try {
|
||||
await requestReturn({
|
||||
sourceSiteId: requestForm.sourceSiteId,
|
||||
targetWarehouseId: requestForm.targetWarehouseId,
|
||||
palletIds: requestScannedPallets.value.map((item) => item.palletId),
|
||||
idempotencyKey: ensureRequestIdempotencyKey(),
|
||||
});
|
||||
requestIdempotencyKey.value = "";
|
||||
proxy.$modal.msgSuccess("退回申请已提交");
|
||||
resetRequestOperation();
|
||||
loadReturnList();
|
||||
} catch (error) {
|
||||
requestError.value = `${errorMessage(error, "退回申请提交失败")};直接重试将复用本次幂等键`;
|
||||
} finally {
|
||||
requestSubmitLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetConfirmOperation() {
|
||||
confirmSession += 1;
|
||||
confirmScanGeneration += 1;
|
||||
detailGeneration += 1;
|
||||
confirmForm.returnId = null;
|
||||
confirmExpectedRows.value = [];
|
||||
confirmExpectedPalletIds.value = [];
|
||||
confirmScannedPallets.value = [];
|
||||
confirmIdempotencyKey.value = "";
|
||||
confirmError.value = "";
|
||||
detailLoading.value = false;
|
||||
scanLoading.confirm = false;
|
||||
}
|
||||
|
||||
function handleConfirmSelection(event) {
|
||||
const row = requestedReturns.value[Number(event.detail.value)];
|
||||
selectReturnRow(row);
|
||||
}
|
||||
|
||||
function selectReturnRow(row) {
|
||||
if (!row || row.status !== "REQUESTED" || confirmPending.value) return;
|
||||
resetConfirmOperation();
|
||||
const session = confirmSession;
|
||||
const generation = ++detailGeneration;
|
||||
confirmForm.returnId = row.returnId;
|
||||
detailLoading.value = true;
|
||||
getReturn(row.returnId)
|
||||
.then((response) => {
|
||||
if (session !== confirmSession || generation !== detailGeneration) return;
|
||||
const detail = response.data;
|
||||
if (!detail || detail.status !== "REQUESTED") {
|
||||
confirmError.value = "该退回单已不是待确认状态";
|
||||
return;
|
||||
}
|
||||
confirmExpectedRows.value = (Array.isArray(detail.items) ? detail.items : []).map(
|
||||
(item) => ({
|
||||
palletId: item.palletId,
|
||||
palletCode: item.palletCode || `#${item.palletId}`,
|
||||
}),
|
||||
);
|
||||
confirmExpectedPalletIds.value = confirmExpectedRows.value.map((item) => item.palletId);
|
||||
if (!confirmExpectedPalletIds.value.length) confirmError.value = "该退回单没有托盘明细";
|
||||
})
|
||||
.catch((error) => {
|
||||
if (session === confirmSession && generation === detailGeneration) {
|
||||
confirmError.value = errorMessage(error, "退回单详情加载失败,请稍后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (session === confirmSession && generation === detailGeneration) {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleConfirmScan(qrToken) {
|
||||
if (!confirmForm.returnId || !confirmExpectedPalletIds.value.length || confirmPending.value || scanLoading.confirm) return;
|
||||
const session = confirmSession;
|
||||
const generation = ++confirmScanGeneration;
|
||||
scanLoading.confirm = true;
|
||||
confirmError.value = "";
|
||||
scanReturnPallet(qrToken, { mode: "confirm", returnId: confirmForm.returnId })
|
||||
.then((response) => {
|
||||
if (session !== confirmSession || generation !== confirmScanGeneration) return;
|
||||
const pallet = response.data;
|
||||
if (!confirmExpectedPalletIds.value.includes(pallet.palletId)) {
|
||||
confirmError.value = "该托盘不属于所选退回单";
|
||||
return;
|
||||
}
|
||||
if (confirmScannedPallets.value.some((item) => item.palletId === pallet.palletId)) {
|
||||
proxy.$modal.msg("该托盘已完成扫码");
|
||||
return;
|
||||
}
|
||||
confirmScannedPallets.value.push({
|
||||
palletId: pallet.palletId,
|
||||
palletCode: pallet.palletCode || `#${pallet.palletId}`,
|
||||
});
|
||||
confirmIdempotencyKey.value = "";
|
||||
})
|
||||
.catch((error) => {
|
||||
if (session === confirmSession && generation === confirmScanGeneration) {
|
||||
confirmError.value = errorMessage(error, "到仓托盘扫码校验失败,请检查后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (session === confirmSession && generation === confirmScanGeneration) {
|
||||
scanLoading.confirm = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleConfirmScanInvalid(message) {
|
||||
confirmError.value = message;
|
||||
}
|
||||
|
||||
function handleScanDuplicate() {
|
||||
proxy.$modal.msg("扫码过快,请勿重复扫描");
|
||||
}
|
||||
|
||||
function isConfirmPalletScanned(palletId) {
|
||||
return confirmScannedPallets.value.some((item) => item.palletId === palletId);
|
||||
}
|
||||
|
||||
function sameIdSet(left, right) {
|
||||
if (left.length !== right.length) return false;
|
||||
const leftSet = new Set(left);
|
||||
const rightSet = new Set(right);
|
||||
return (
|
||||
leftSet.size === left.length &&
|
||||
rightSet.size === right.length &&
|
||||
right.every((id) => leftSet.has(id))
|
||||
);
|
||||
}
|
||||
|
||||
function ensureConfirmIdempotencyKey(returnId) {
|
||||
if (!confirmIdempotencyKey.value) confirmIdempotencyKey.value = createIdempotencyKey();
|
||||
return confirmIdempotencyKey.value;
|
||||
}
|
||||
|
||||
async function submitReturnConfirm() {
|
||||
if (confirmPending.value || detailLoading.value || scanLoading.confirm) return;
|
||||
const returnId = confirmForm.returnId;
|
||||
const expectedIds = [...confirmExpectedPalletIds.value];
|
||||
const scannedIds = confirmScannedPallets.value.map((item) => item.palletId);
|
||||
if (!returnId || !sameIdSet(expectedIds, scannedIds)) {
|
||||
confirmError.value = "必须逐个扫描并完整匹配退回单全部托盘";
|
||||
return;
|
||||
}
|
||||
confirmSubmitLoading.value = true;
|
||||
confirmError.value = "";
|
||||
try {
|
||||
const latest = (await getReturn(returnId)).data;
|
||||
const latestIds = (Array.isArray(latest.items) ? latest.items : []).map((item) => item.palletId);
|
||||
if (latest.status !== "REQUESTED" || !sameIdSet(latestIds, scannedIds)) {
|
||||
confirmError.value = "退回单状态或托盘明细已变化,请重新选择并扫码";
|
||||
return;
|
||||
}
|
||||
await confirmReturn(returnId, {
|
||||
scannedPalletIds: scannedIds,
|
||||
idempotencyKey: ensureConfirmIdempotencyKey(returnId),
|
||||
});
|
||||
confirmIdempotencyKey.value = "";
|
||||
proxy.$modal.msgSuccess("退回确认成功");
|
||||
resetConfirmOperation();
|
||||
loadReturnList();
|
||||
} catch (error) {
|
||||
confirmError.value = `${errorMessage(error, "退回确认失败")};直接重试将复用本次幂等键`;
|
||||
} finally {
|
||||
confirmSubmitLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadReturnList();
|
||||
loadWarehouseOptions();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,549 @@
|
||||
<template>
|
||||
<view class="min-h-screen bg-gray-50">
|
||||
<scroll-view scroll-y class="h-screen">
|
||||
<view class="space-y-4 p-4 pb-24">
|
||||
<view class="flex items-center justify-between">
|
||||
<view>
|
||||
<text class="block text-xl font-bold text-gray-800">发货出库</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
选择来源仓库和目标现场,扫码创建整托发货单
|
||||
</text>
|
||||
</view>
|
||||
<button
|
||||
v-if="canDraft"
|
||||
class="rounded-lg bg-orange-500 px-3 py-2 text-xs text-white active:opacity-80"
|
||||
size="mini"
|
||||
:disabled="operationPending"
|
||||
@click="startNewDraft"
|
||||
>
|
||||
新建草稿
|
||||
</button>
|
||||
</view>
|
||||
<TestBatchBanner />
|
||||
|
||||
<view v-if="!canPage" class="rounded-2xl bg-white p-6 text-center shadow-sm">
|
||||
<text class="text-sm text-gray-500">当前账号没有发货操作权限</text>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<view v-if="editorVisible" class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-4 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">
|
||||
{{ editing ? "编辑发货草稿" : "新建发货草稿" }}
|
||||
</text>
|
||||
<text class="text-xs text-gray-400">托盘数量 {{ scannedPallets.length }}</text>
|
||||
</view>
|
||||
|
||||
<view class="mb-3">
|
||||
<text class="mb-2 block text-xs text-gray-500">来源仓库</text>
|
||||
<picker
|
||||
:range="warehouseOptions"
|
||||
range-key="name"
|
||||
:value="warehouseIndex"
|
||||
:disabled="optionLoading || detailLoading || operationPending"
|
||||
@change="handleWarehouseChange"
|
||||
>
|
||||
<view class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700">
|
||||
{{ selectedWarehouseName || "请选择来源仓库" }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="mb-3">
|
||||
<text class="mb-2 block text-xs text-gray-500">目标客户现场</text>
|
||||
<picker
|
||||
:range="siteOptions"
|
||||
range-key="name"
|
||||
:value="siteIndex"
|
||||
:disabled="optionLoading || detailLoading || operationPending"
|
||||
@change="handleSiteChange"
|
||||
>
|
||||
<view class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700">
|
||||
{{ selectedSiteName || "请选择目标现场" }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="mb-3">
|
||||
<text class="mb-2 block text-xs text-gray-500">连续扫码</text>
|
||||
<PalletScanInput
|
||||
:disabled="!form.sourceWarehouseId || !form.targetSiteId || detailLoading || operationPending"
|
||||
:loading="scanLoading"
|
||||
@scan="handleShipmentScan"
|
||||
@invalid="handleScanInvalid"
|
||||
@duplicate="handleScanDuplicate"
|
||||
/>
|
||||
<text class="mt-2 block text-xs text-gray-400">
|
||||
仅接受来源仓库内有剩余包数的 FILLED_IN_WAREHOUSE 托盘。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<text v-if="scanError" class="mb-3 block text-xs text-red-500">{{ scanError }}</text>
|
||||
<text v-if="formError" class="mb-3 block text-xs text-red-500">{{ formError }}</text>
|
||||
|
||||
<view v-if="scannedPallets.length" class="mb-4 space-y-2">
|
||||
<view
|
||||
v-for="pallet in scannedPallets"
|
||||
:key="pallet.palletId"
|
||||
class="rounded-xl bg-orange-50 px-3 py-2"
|
||||
>
|
||||
<view class="flex items-center justify-between">
|
||||
<view>
|
||||
<text class="block text-sm font-medium text-gray-700">
|
||||
{{ pallet.palletCode || `#${pallet.palletId}` }}
|
||||
</text>
|
||||
<text class="block text-xs text-gray-500">
|
||||
{{ pallet.strainName || pallet.strainCode || "未提供菌种" }} · 剩余 {{ pallet.remainingPackages }} 包
|
||||
</text>
|
||||
</view>
|
||||
<button
|
||||
class="text-xs text-red-500"
|
||||
size="mini"
|
||||
:disabled="operationPending || detailLoading"
|
||||
@click="removeScannedPallet(pallet.palletId)"
|
||||
>
|
||||
移除
|
||||
</button>
|
||||
</view>
|
||||
<text class="mt-1 block text-xs text-gray-400">
|
||||
批次:{{ pallet.batchNo || "—" }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex gap-3">
|
||||
<button
|
||||
class="flex-1 rounded-xl bg-orange-500 py-3 text-sm text-white active:opacity-80 disabled:bg-gray-300"
|
||||
:disabled="!canSaveDraft || operationPending || scanLoading || detailLoading"
|
||||
@click="saveDraft"
|
||||
>
|
||||
{{ submitLoading ? "保存中" : editing ? "保存修改" : "保存草稿" }}
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 rounded-xl bg-gray-100 py-3 text-sm text-gray-600 active:opacity-80"
|
||||
:disabled="operationPending"
|
||||
@click="cancelEditor"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">发货单</text>
|
||||
<button
|
||||
class="text-xs text-blue-500"
|
||||
size="mini"
|
||||
:disabled="loading || operationPending"
|
||||
@click="loadShipmentList"
|
||||
>
|
||||
刷新
|
||||
</button>
|
||||
</view>
|
||||
<text v-if="listError" class="mb-3 block text-xs text-red-500">{{ listError }}</text>
|
||||
<text v-if="confirmError" class="mb-3 block text-xs text-red-500">{{ confirmError }}</text>
|
||||
<view v-if="loading" class="py-6 text-center text-xs text-gray-400">加载中...</view>
|
||||
<view v-else-if="!shipmentList.length" class="py-6 text-center text-xs text-gray-400">
|
||||
暂无发货单
|
||||
</view>
|
||||
<view
|
||||
v-for="row in shipmentList"
|
||||
v-else
|
||||
:key="row.shipmentId"
|
||||
class="mb-3 rounded-xl border border-gray-100 p-3 last:mb-0"
|
||||
>
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="font-medium text-gray-700">{{ row.shipmentNo || `#${row.shipmentId}` }}</text>
|
||||
<text :class="row.status === 'DRAFT' ? 'text-orange-500' : 'text-green-500'" class="text-xs">
|
||||
{{ row.status === "DRAFT" ? "草稿" : "已确认" }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
{{ warehouseName(row.sourceWarehouseId) }} → {{ siteName(row.targetSiteId) }} · 托盘 {{ row.items?.length || 0 }} 个
|
||||
</text>
|
||||
<view v-if="row.status === 'DRAFT'" class="mt-3 flex gap-2">
|
||||
<button
|
||||
v-if="canEdit"
|
||||
class="flex-1 rounded-lg bg-blue-50 py-2 text-xs text-blue-600"
|
||||
size="mini"
|
||||
:disabled="operationPending"
|
||||
@click="startEditDraft(row)"
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
v-if="canConfirm"
|
||||
class="flex-1 rounded-lg bg-green-50 py-2 text-xs text-green-600"
|
||||
size="mini"
|
||||
:disabled="operationPending"
|
||||
@click="confirmDraft(row)"
|
||||
>
|
||||
{{ confirmLoading ? "确认中" : "确认" }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, reactive, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import PalletScanInput from "@/components/business/PalletScanInput.vue";
|
||||
import TestBatchBanner from "@/components/business/TestBatchBanner.vue";
|
||||
import {
|
||||
addShipment,
|
||||
confirmShipment,
|
||||
getShipment,
|
||||
getShipmentSiteOptions,
|
||||
getShipmentWarehouseOptions,
|
||||
listShipment,
|
||||
scanShipmentPallet,
|
||||
updateShipment,
|
||||
} from "@/api/business/shipment";
|
||||
import { createIdempotencyKey } from "@/utils/palletScan";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const shipmentList = ref([]);
|
||||
const warehouseOptions = ref([]);
|
||||
const siteOptions = ref([]);
|
||||
const scannedPallets = ref([]);
|
||||
const editorVisible = ref(false);
|
||||
const editing = ref(false);
|
||||
const loading = ref(false);
|
||||
const optionLoading = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const scanLoading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const confirmLoading = ref(false);
|
||||
const listError = ref("");
|
||||
const formError = ref("");
|
||||
const scanError = ref("");
|
||||
const confirmError = ref("");
|
||||
const form = reactive({ shipmentId: null, sourceWarehouseId: null, targetSiteId: null });
|
||||
const confirmState = reactive({ shipmentId: null, key: "" });
|
||||
let listGeneration = 0;
|
||||
let scanGeneration = 0;
|
||||
|
||||
const canPage = computed(() =>
|
||||
proxy.$auth.hasPermiOr([
|
||||
"business:shipment:list",
|
||||
"business:shipment:query",
|
||||
"business:shipment:add",
|
||||
"business:shipment:edit",
|
||||
"business:shipment:confirm",
|
||||
]),
|
||||
);
|
||||
const canList = computed(() => proxy.$auth.hasPermi("business:shipment:list"));
|
||||
const canDraft = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:shipment:query", "business:shipment:add"]),
|
||||
);
|
||||
const canEdit = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:shipment:query", "business:shipment:edit"]),
|
||||
);
|
||||
const canConfirm = computed(() => proxy.$auth.hasPermi("business:shipment:confirm"));
|
||||
const canSaveDraft = computed(() => (editing.value ? canEdit.value : canDraft.value));
|
||||
const operationPending = computed(
|
||||
() =>
|
||||
submitLoading.value ||
|
||||
confirmLoading.value ||
|
||||
detailLoading.value,
|
||||
);
|
||||
const warehouseIndex = computed(() => {
|
||||
const index = warehouseOptions.value.findIndex(
|
||||
(item) => item.id === form.sourceWarehouseId,
|
||||
);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
const siteIndex = computed(() => {
|
||||
const index = siteOptions.value.findIndex((item) => item.id === form.targetSiteId);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
const selectedWarehouseName = computed(
|
||||
() => warehouseOptions.value.find((item) => item.id === form.sourceWarehouseId)?.name || "",
|
||||
);
|
||||
const selectedSiteName = computed(
|
||||
() => siteOptions.value.find((item) => item.id === form.targetSiteId)?.name || "",
|
||||
);
|
||||
|
||||
function errorMessage(error, fallback) {
|
||||
return error?.response?.data?.msg || error?.message || fallback;
|
||||
}
|
||||
|
||||
function warehouseName(id) {
|
||||
return warehouseOptions.value.find((item) => item.id === id)?.name || `仓库 #${id || "-"}`;
|
||||
}
|
||||
|
||||
function siteName(id) {
|
||||
return siteOptions.value.find((item) => item.id === id)?.name || `现场 #${id || "-"}`;
|
||||
}
|
||||
|
||||
function resetEditor() {
|
||||
scanGeneration += 1;
|
||||
editorVisible.value = false;
|
||||
editing.value = false;
|
||||
form.shipmentId = null;
|
||||
form.sourceWarehouseId = null;
|
||||
form.targetSiteId = null;
|
||||
scannedPallets.value = [];
|
||||
detailLoading.value = false;
|
||||
scanLoading.value = false;
|
||||
formError.value = "";
|
||||
scanError.value = "";
|
||||
}
|
||||
|
||||
function loadShipmentList() {
|
||||
if (!canList.value) return;
|
||||
const generation = ++listGeneration;
|
||||
loading.value = true;
|
||||
listError.value = "";
|
||||
listShipment({ pageNum: 1, pageSize: 20 })
|
||||
.then((response) => {
|
||||
if (generation !== listGeneration) return;
|
||||
shipmentList.value = Array.isArray(response.rows) ? response.rows : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation !== listGeneration) return;
|
||||
shipmentList.value = [];
|
||||
listError.value = errorMessage(error, "发货单列表加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === listGeneration) loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function loadOptions() {
|
||||
if (!proxy.$auth.hasPermi("business:shipment:query")) return;
|
||||
optionLoading.value = true;
|
||||
Promise.all([getShipmentWarehouseOptions(), getShipmentSiteOptions()])
|
||||
.then(([warehouseResponse, siteResponse]) => {
|
||||
warehouseOptions.value = Array.isArray(warehouseResponse.data)
|
||||
? warehouseResponse.data
|
||||
: [];
|
||||
siteOptions.value = Array.isArray(siteResponse.data) ? siteResponse.data : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
warehouseOptions.value = [];
|
||||
siteOptions.value = [];
|
||||
formError.value = errorMessage(error, "仓库或现场选项加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
optionLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function startNewDraft() {
|
||||
if (!canDraft.value || operationPending.value) return;
|
||||
resetEditor();
|
||||
editorVisible.value = true;
|
||||
loadOptions();
|
||||
}
|
||||
|
||||
function startEditDraft(row) {
|
||||
if (!canEdit.value || row.status !== "DRAFT" || operationPending.value) return;
|
||||
resetEditor();
|
||||
editorVisible.value = true;
|
||||
editing.value = true;
|
||||
form.shipmentId = row.shipmentId;
|
||||
loadOptions();
|
||||
detailLoading.value = true;
|
||||
getShipment(row.shipmentId)
|
||||
.then((response) => {
|
||||
const detail = response.data;
|
||||
if (!detail || detail.status !== "DRAFT") {
|
||||
formError.value = "该发货单已不是草稿状态,不能编辑";
|
||||
return;
|
||||
}
|
||||
form.sourceWarehouseId = detail.sourceWarehouseId;
|
||||
form.targetSiteId = detail.targetSiteId;
|
||||
scannedPallets.value = (Array.isArray(detail.items) ? detail.items : []).map(
|
||||
(item) => ({
|
||||
palletId: item.palletId,
|
||||
palletCode: item.palletCode || `#${item.palletId}`,
|
||||
strainName: item.strainName || item.strainCode,
|
||||
batchNo: item.batchNo,
|
||||
remainingPackages: item.remainingPackages ?? item.packageQty ?? 0,
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
formError.value = errorMessage(error, "发货单详情加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
detailLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function cancelEditor() {
|
||||
if (operationPending.value) return;
|
||||
resetEditor();
|
||||
}
|
||||
|
||||
function clearScannedPallets() {
|
||||
scannedPallets.value = [];
|
||||
scanError.value = "";
|
||||
scanGeneration += 1;
|
||||
}
|
||||
|
||||
function handleWarehouseChange(event) {
|
||||
const option = warehouseOptions.value[Number(event.detail.value)];
|
||||
if (!option || option.id === form.sourceWarehouseId) return;
|
||||
form.sourceWarehouseId = option.id;
|
||||
clearScannedPallets();
|
||||
}
|
||||
|
||||
function handleSiteChange(event) {
|
||||
const option = siteOptions.value[Number(event.detail.value)];
|
||||
if (!option || option.id === form.targetSiteId) return;
|
||||
form.targetSiteId = option.id;
|
||||
clearScannedPallets();
|
||||
}
|
||||
|
||||
function handleShipmentScan(qrToken) {
|
||||
if (
|
||||
!form.sourceWarehouseId ||
|
||||
!form.targetSiteId ||
|
||||
operationPending.value ||
|
||||
scanLoading.value
|
||||
) {
|
||||
scanError.value = "请先选择来源仓库和目标现场";
|
||||
return;
|
||||
}
|
||||
const generation = ++scanGeneration;
|
||||
scanLoading.value = true;
|
||||
scanError.value = "";
|
||||
scanShipmentPallet(qrToken, {
|
||||
sourceWarehouseId: form.sourceWarehouseId,
|
||||
targetSiteId: form.targetSiteId,
|
||||
})
|
||||
.then((response) => {
|
||||
if (generation !== scanGeneration) return;
|
||||
const pallet = response.data;
|
||||
if (pallet?.flowState !== "FILLED_IN_WAREHOUSE" || pallet.remainingPackages <= 0) {
|
||||
scanError.value = "该托盘不是来源仓库内的有料托盘,不能发货";
|
||||
return;
|
||||
}
|
||||
if (scannedPallets.value.some((item) => item.palletId === pallet.palletId)) {
|
||||
proxy.$modal.msg("该托盘已在当前发货草稿中");
|
||||
return;
|
||||
}
|
||||
scannedPallets.value.push({
|
||||
palletId: pallet.palletId,
|
||||
palletCode: pallet.palletCode || `#${pallet.palletId}`,
|
||||
strainName: pallet.strainName || pallet.strainCode,
|
||||
batchNo: pallet.batchNo,
|
||||
remainingPackages: pallet.remainingPackages,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation === scanGeneration) {
|
||||
scanError.value = errorMessage(error, "托盘扫码校验失败,请检查后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === scanGeneration) scanLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleScanInvalid(message) {
|
||||
scanError.value = message;
|
||||
}
|
||||
|
||||
function handleScanDuplicate() {
|
||||
proxy.$modal.msg("扫码过快,请勿重复扫描");
|
||||
}
|
||||
|
||||
function removeScannedPallet(palletId) {
|
||||
if (operationPending.value) return;
|
||||
scannedPallets.value = scannedPallets.value.filter(
|
||||
(item) => item.palletId !== palletId,
|
||||
);
|
||||
}
|
||||
|
||||
function saveDraft() {
|
||||
if (!canSaveDraft.value || operationPending.value || scanLoading.value) return;
|
||||
if (!form.sourceWarehouseId || !form.targetSiteId) {
|
||||
formError.value = "请选择来源仓库和目标现场";
|
||||
return;
|
||||
}
|
||||
if (!scannedPallets.value.length) {
|
||||
formError.value = "请至少扫描一个有料托盘";
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
formError.value = "";
|
||||
const payload = {
|
||||
sourceWarehouseId: form.sourceWarehouseId,
|
||||
targetSiteId: form.targetSiteId,
|
||||
palletIds: scannedPallets.value.map((item) => item.palletId),
|
||||
};
|
||||
const submitRequest = editing.value
|
||||
? updateShipment({ ...payload, shipmentId: form.shipmentId })
|
||||
: addShipment(payload);
|
||||
submitRequest
|
||||
.then((response) => {
|
||||
proxy.$modal.msgSuccess(response.msg || "发货草稿保存成功");
|
||||
resetEditor();
|
||||
loadShipmentList();
|
||||
})
|
||||
.catch((error) => {
|
||||
formError.value = errorMessage(error, "发货草稿保存失败,请根据提示修正后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function askConfirm(content) {
|
||||
return new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title: "系统提示",
|
||||
content,
|
||||
cancelText: "取消",
|
||||
confirmText: "确定",
|
||||
success: (result) => resolve(Boolean(result.confirm)),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function ensureConfirmIdempotencyKey(shipmentId) {
|
||||
if (confirmState.shipmentId !== shipmentId) {
|
||||
confirmState.shipmentId = shipmentId;
|
||||
confirmState.key = "";
|
||||
}
|
||||
if (!confirmState.key) confirmState.key = createIdempotencyKey();
|
||||
return confirmState.key;
|
||||
}
|
||||
|
||||
function clearConfirmIdempotency() {
|
||||
confirmState.shipmentId = null;
|
||||
confirmState.key = "";
|
||||
}
|
||||
|
||||
async function confirmDraft(row) {
|
||||
if (!canConfirm.value || row.status !== "DRAFT" || operationPending.value) return;
|
||||
if (!(await askConfirm(`确认发货单“${row.shipmentNo || `#${row.shipmentId}`}”?`))) return;
|
||||
confirmLoading.value = true;
|
||||
confirmError.value = "";
|
||||
try {
|
||||
await confirmShipment(row.shipmentId, {
|
||||
idempotencyKey: ensureConfirmIdempotencyKey(row.shipmentId),
|
||||
});
|
||||
clearConfirmIdempotency();
|
||||
proxy.$modal.msgSuccess("发货确认成功");
|
||||
loadShipmentList();
|
||||
} catch (error) {
|
||||
confirmError.value = `${errorMessage(error, "发货确认失败")};再次确认将复用本次幂等键`;
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadShipmentList();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,380 @@
|
||||
<template>
|
||||
<view class="min-h-screen bg-gray-50">
|
||||
<scroll-view scroll-y class="h-screen">
|
||||
<view class="space-y-4 p-4 pb-24">
|
||||
<view>
|
||||
<text class="block text-xl font-bold text-gray-800">客户现场使用</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
扫描现场托盘,首次使用绑定产线并记录本次消耗包数
|
||||
</text>
|
||||
</view>
|
||||
<TestBatchBanner />
|
||||
|
||||
<view v-if="!canPage" class="rounded-2xl bg-white p-6 text-center shadow-sm">
|
||||
<text class="text-sm text-gray-500">当前账号没有现场使用权限</text>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">扫描待使用托盘</text>
|
||||
<button class="text-xs text-gray-500" size="mini" :disabled="operationPending" @click="resetUsageOperation">
|
||||
重新扫描
|
||||
</button>
|
||||
</view>
|
||||
<PalletScanInput
|
||||
:disabled="operationPending || optionLoading"
|
||||
:loading="scanLoading"
|
||||
@scan="handleUsageScan"
|
||||
@invalid="handleScanInvalid"
|
||||
@duplicate="handleScanDuplicate"
|
||||
/>
|
||||
<text class="mt-2 block text-xs text-gray-400">
|
||||
仅接受 AT_SITE_UNUSED 或 AT_SITE_PARTIAL 且仍有余量的托盘。
|
||||
</text>
|
||||
<text v-if="operationError" class="mt-2 block text-xs text-red-500">{{ operationError }}</text>
|
||||
|
||||
<view v-if="scannedPallet" class="mt-4 rounded-xl bg-purple-50 p-3">
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="font-medium text-gray-700">
|
||||
{{ scannedPallet.palletCode || `#${scannedPallet.palletId}` }}
|
||||
</text>
|
||||
<text class="text-xs text-purple-600">{{ scannedPallet.flowState }}</text>
|
||||
</view>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
{{ scannedPallet.strainName || scannedPallet.strainCode || "未提供菌种" }} · 批次 {{ scannedPallet.batchNo || "—" }}
|
||||
</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
当前剩余:{{ scannedPallet.remainingPackages }} 包 · 现场 #{{ scannedPallet.siteId }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<text class="mb-3 block font-bold text-gray-800">使用信息</text>
|
||||
<view class="mb-3">
|
||||
<text class="mb-2 block text-xs text-gray-500">使用产线</text>
|
||||
<view v-if="lineLocked" class="rounded-xl bg-gray-100 px-3 py-3 text-sm text-gray-600">
|
||||
{{ selectedLineName }}(已由托盘绑定)
|
||||
</view>
|
||||
<picker
|
||||
v-else
|
||||
:range="lineOptions"
|
||||
range-key="name"
|
||||
:value="lineIndex"
|
||||
:disabled="operationPending || optionLoading || !scannedPallet"
|
||||
@change="handleLineChange"
|
||||
>
|
||||
<view class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700">
|
||||
{{ selectedLineName || "请选择现场产线" }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view>
|
||||
<text class="mb-2 block text-xs text-gray-500">本次使用包数</text>
|
||||
<input
|
||||
v-model="form.usedPackages"
|
||||
class="rounded-xl bg-gray-50 px-3 py-3 text-sm text-gray-700"
|
||||
type="number"
|
||||
placeholder="请输入正整数"
|
||||
:disabled="operationPending || !scannedPallet"
|
||||
@input="markPayloadChanged"
|
||||
/>
|
||||
<text v-if="scannedPallet" class="mt-1 block text-xs text-gray-400">
|
||||
不能超过当前剩余 {{ scannedPallet.remainingPackages }} 包,最终以服务端校验为准
|
||||
</text>
|
||||
</view>
|
||||
<text v-if="submitError" class="mt-3 block text-xs text-red-500">{{ submitError }}</text>
|
||||
<view v-if="usageResult" class="mt-3 rounded-xl bg-green-50 p-3">
|
||||
<text class="block text-sm font-medium text-green-700">使用记录已提交</text>
|
||||
<text class="mt-1 block text-xs text-gray-600">
|
||||
使用后剩余 {{ usageResult.remainingPackages }} 包 · 状态 {{ usageResult.flowState }}
|
||||
</text>
|
||||
</view>
|
||||
<button
|
||||
class="mt-4 w-full rounded-xl bg-purple-500 py-3 text-sm text-white active:opacity-80 disabled:bg-gray-300"
|
||||
:disabled="!canOperate || operationPending || scanLoading || optionLoading || !scannedPallet || !!usageResult"
|
||||
@click="submitUsage"
|
||||
>
|
||||
{{ submitLoading ? "提交中" : "确认使用" }}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
||||
<view class="mb-3 flex items-center justify-between">
|
||||
<text class="font-bold text-gray-800">最近使用记录</text>
|
||||
<button class="text-xs text-blue-500" size="mini" :disabled="loading" @click="loadUsageList">
|
||||
刷新
|
||||
</button>
|
||||
</view>
|
||||
<text v-if="listError" class="mb-3 block text-xs text-red-500">{{ listError }}</text>
|
||||
<view v-if="loading" class="py-6 text-center text-xs text-gray-400">加载中...</view>
|
||||
<view v-else-if="!usageList.length" class="py-6 text-center text-xs text-gray-400">
|
||||
暂无使用记录
|
||||
</view>
|
||||
<view
|
||||
v-for="row in usageList"
|
||||
v-else
|
||||
:key="row.usageId"
|
||||
class="mb-3 rounded-xl border border-gray-100 p-3 last:mb-0"
|
||||
>
|
||||
<text class="block font-medium text-gray-700">{{ row.usageNo || `#${row.usageId}` }}</text>
|
||||
<text class="mt-1 block text-xs text-gray-500">
|
||||
托盘 #{{ row.palletId }} · 产线 #{{ row.lineId }} · 使用 {{ row.usedPackages }} 包
|
||||
</text>
|
||||
<text class="mt-1 block text-xs text-gray-400">
|
||||
剩余 {{ row.remainingPackagesAfter }} 包 · {{ row.occurredAt || "—" }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, reactive, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import PalletScanInput from "@/components/business/PalletScanInput.vue";
|
||||
import TestBatchBanner from "@/components/business/TestBatchBanner.vue";
|
||||
import {
|
||||
addUsage,
|
||||
getUsageLineOptions,
|
||||
listUsage,
|
||||
scanUsagePallet,
|
||||
} from "@/api/business/usage";
|
||||
import { createIdempotencyKey } from "@/utils/palletScan";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const usageList = ref([]);
|
||||
const lineOptions = ref([]);
|
||||
const scannedPallet = ref(null);
|
||||
const usageResult = ref(null);
|
||||
const lineLocked = ref(false);
|
||||
const loading = ref(false);
|
||||
const optionLoading = ref(false);
|
||||
const scanLoading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const listError = ref("");
|
||||
const operationError = ref("");
|
||||
const submitError = ref("");
|
||||
const idempotencyKey = ref("");
|
||||
const form = reactive({ qrToken: "", lineId: null, usedPackages: "" });
|
||||
let listGeneration = 0;
|
||||
let scanGeneration = 0;
|
||||
let optionGeneration = 0;
|
||||
let operationSession = 0;
|
||||
|
||||
const canPage = computed(() =>
|
||||
proxy.$auth.hasPermiOr([
|
||||
"business:usage:list",
|
||||
"business:usage:query",
|
||||
"business:usage:add",
|
||||
]),
|
||||
);
|
||||
const canList = computed(() => proxy.$auth.hasPermi("business:usage:list"));
|
||||
const canOperate = computed(() =>
|
||||
proxy.$auth.hasPermiAnd(["business:usage:query", "business:usage:add"]),
|
||||
);
|
||||
const operationPending = computed(() => submitLoading.value);
|
||||
const lineIndex = computed(() => {
|
||||
const index = lineOptions.value.findIndex((item) => item.id === form.lineId);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
const selectedLineName = computed(
|
||||
() => lineOptions.value.find((item) => item.id === form.lineId)?.name || "",
|
||||
);
|
||||
|
||||
function errorMessage(error, fallback) {
|
||||
return error?.response?.data?.msg || error?.message || fallback;
|
||||
}
|
||||
|
||||
function loadUsageList() {
|
||||
if (!canList.value) return;
|
||||
const generation = ++listGeneration;
|
||||
loading.value = true;
|
||||
listError.value = "";
|
||||
listUsage({ pageNum: 1, pageSize: 20 })
|
||||
.then((response) => {
|
||||
if (generation !== listGeneration) return;
|
||||
usageList.value = Array.isArray(response.rows) ? response.rows : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation !== listGeneration) return;
|
||||
usageList.value = [];
|
||||
listError.value = errorMessage(error, "使用记录加载失败,请稍后重试");
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === listGeneration) loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetUsageOperation() {
|
||||
operationSession += 1;
|
||||
scanGeneration += 1;
|
||||
optionGeneration += 1;
|
||||
scannedPallet.value = null;
|
||||
usageResult.value = null;
|
||||
lineOptions.value = [];
|
||||
lineLocked.value = false;
|
||||
idempotencyKey.value = "";
|
||||
form.qrToken = "";
|
||||
form.lineId = null;
|
||||
form.usedPackages = "";
|
||||
operationError.value = "";
|
||||
submitError.value = "";
|
||||
scanLoading.value = false;
|
||||
optionLoading.value = false;
|
||||
}
|
||||
|
||||
function loadLineOptions(siteId, session, scanRequestGeneration) {
|
||||
const generation = ++optionGeneration;
|
||||
optionLoading.value = true;
|
||||
getUsageLineOptions(siteId)
|
||||
.then((response) => {
|
||||
if (
|
||||
generation !== optionGeneration ||
|
||||
session !== operationSession ||
|
||||
scanRequestGeneration !== scanGeneration
|
||||
) {
|
||||
return;
|
||||
}
|
||||
lineOptions.value = Array.isArray(response.data) ? response.data : [];
|
||||
if (!lineOptions.value.length) operationError.value = "该现场暂无可用产线";
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation !== optionGeneration || session !== operationSession) return;
|
||||
lineOptions.value = [];
|
||||
operationError.value = errorMessage(error, "产线选项加载失败,请稍后重新扫码");
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === optionGeneration && session === operationSession) {
|
||||
optionLoading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleUsageScan(qrToken) {
|
||||
if (operationPending.value || scanLoading.value || optionLoading.value) return;
|
||||
resetUsageOperation();
|
||||
const session = operationSession;
|
||||
const generation = ++scanGeneration;
|
||||
scanLoading.value = true;
|
||||
scanUsagePallet(qrToken)
|
||||
.then((response) => {
|
||||
if (generation !== scanGeneration || session !== operationSession) return;
|
||||
const pallet = response.data;
|
||||
if (
|
||||
!["AT_SITE_UNUSED", "AT_SITE_PARTIAL"].includes(pallet?.flowState) ||
|
||||
pallet.remainingPackages <= 0
|
||||
) {
|
||||
operationError.value = "该托盘当前不可记录使用";
|
||||
return;
|
||||
}
|
||||
scannedPallet.value = {
|
||||
palletId: pallet.palletId,
|
||||
palletCode: pallet.palletCode || `#${pallet.palletId}`,
|
||||
siteId: pallet.siteId,
|
||||
currentLineId: pallet.currentLineId,
|
||||
strainCode: pallet.strainCode,
|
||||
strainName: pallet.strainName,
|
||||
batchNo: pallet.batchNo,
|
||||
remainingPackages: pallet.remainingPackages,
|
||||
flowState: pallet.flowState,
|
||||
};
|
||||
form.qrToken = qrToken;
|
||||
if (pallet.currentLineId != null) {
|
||||
lineLocked.value = true;
|
||||
form.lineId = pallet.currentLineId;
|
||||
lineOptions.value = [
|
||||
{ id: pallet.currentLineId, code: `#${pallet.currentLineId}`, name: `产线 #${pallet.currentLineId}` },
|
||||
];
|
||||
} else {
|
||||
lineLocked.value = false;
|
||||
form.lineId = null;
|
||||
loadLineOptions(pallet.siteId, session, generation);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (generation === scanGeneration && session === operationSession) {
|
||||
operationError.value = errorMessage(error, "托盘扫码校验失败,请检查后重试");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (generation === scanGeneration && session === operationSession) {
|
||||
scanLoading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleScanInvalid(message) {
|
||||
resetUsageOperation();
|
||||
operationError.value = message;
|
||||
}
|
||||
|
||||
function handleScanDuplicate() {
|
||||
proxy.$modal.msg("扫码过快,请勿重复扫描");
|
||||
}
|
||||
|
||||
function handleLineChange(event) {
|
||||
if (lineLocked.value) return;
|
||||
const option = lineOptions.value[Number(event.detail.value)];
|
||||
form.lineId = option?.id || null;
|
||||
markPayloadChanged();
|
||||
}
|
||||
|
||||
function markPayloadChanged() {
|
||||
if (!submitLoading.value) idempotencyKey.value = "";
|
||||
}
|
||||
|
||||
function ensureIdempotencyKey() {
|
||||
if (!idempotencyKey.value) idempotencyKey.value = createIdempotencyKey();
|
||||
return idempotencyKey.value;
|
||||
}
|
||||
|
||||
function submitUsage() {
|
||||
if (!canOperate.value || operationPending.value || scanLoading.value || optionLoading.value) return;
|
||||
const usedPackages = Number(form.usedPackages);
|
||||
if (!scannedPallet.value || !form.qrToken) {
|
||||
submitError.value = "请先扫描待使用托盘";
|
||||
return;
|
||||
}
|
||||
if (!form.lineId) {
|
||||
submitError.value = "请选择使用产线";
|
||||
return;
|
||||
}
|
||||
if (!Number.isInteger(usedPackages) || usedPackages <= 0) {
|
||||
submitError.value = "使用包数必须为正整数";
|
||||
return;
|
||||
}
|
||||
if (usedPackages > scannedPallet.value.remainingPackages) {
|
||||
submitError.value = "使用包数不能超过当前剩余包数";
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
submitError.value = "";
|
||||
addUsage({
|
||||
qrToken: form.qrToken,
|
||||
lineId: form.lineId,
|
||||
usedPackages,
|
||||
idempotencyKey: ensureIdempotencyKey(),
|
||||
})
|
||||
.then((response) => {
|
||||
usageResult.value = response.data;
|
||||
idempotencyKey.value = "";
|
||||
proxy.$modal.msgSuccess(response.msg || "现场使用记录成功");
|
||||
loadUsageList();
|
||||
})
|
||||
.catch((error) => {
|
||||
submitError.value = `${errorMessage(error, "现场使用提交失败")};直接重试将复用本次幂等键,修改输入后会生成新键`;
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadUsageList();
|
||||
});
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,118 +1,94 @@
|
||||
<template>
|
||||
<view
|
||||
class="flex h-full flex-col items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 px-6 pb-20 overflow-hidden"
|
||||
>
|
||||
<!-- Logo Section -->
|
||||
<view class="mb-6 flex flex-col items-center">
|
||||
<view
|
||||
class="mb-4 flex size-16 items-center justify-center rounded-2xl bg-white shadow-lg"
|
||||
>
|
||||
<view class="login-page">
|
||||
<image
|
||||
class="size-10"
|
||||
class="login-background"
|
||||
src="/static/login-background.jpg"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="login-scrim" />
|
||||
|
||||
<view class="login-content">
|
||||
<view class="login-card">
|
||||
<view class="brand-lockup">
|
||||
<view class="brand-logo-frame">
|
||||
<image
|
||||
class="brand-logo"
|
||||
:src="globalConfig.appInfo.logo"
|
||||
mode="widthFix"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-xl font-bold tracking-wide text-gray-800"
|
||||
>智链云枢管理系统登录</text
|
||||
>
|
||||
<text class="brand-title">{{ globalConfig.appInfo.name }}</text>
|
||||
<text class="brand-subtitle">仓储运营管理平台</text>
|
||||
</view>
|
||||
|
||||
<!-- Form Section -->
|
||||
<view class="w-full rounded-3xl bg-white/80 p-6 shadow-xl backdrop-blur-md">
|
||||
<!-- Username -->
|
||||
<view class="group relative mb-5">
|
||||
<view
|
||||
class="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 transition-colors group-focus-within:text-blue-500"
|
||||
>
|
||||
<view class="i-mdi-account text-xl"></view>
|
||||
</view>
|
||||
<view class="login-form">
|
||||
<view class="field-control">
|
||||
<view class="field-icon i-mdi-account" />
|
||||
<input
|
||||
v-model="loginForm.username"
|
||||
class="h-12 w-full rounded-xl bg-gray-50 pl-12 pr-4 text-sm text-gray-700 outline-none transition-all focus:bg-white focus:ring-2 focus:ring-blue-400"
|
||||
class="field-input"
|
||||
type="text"
|
||||
placeholder="请输入账号"
|
||||
placeholder="请输入您的账号"
|
||||
maxlength="30"
|
||||
confirm-type="next"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- Password -->
|
||||
<view class="group relative mb-5">
|
||||
<view
|
||||
class="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 transition-colors group-focus-within:text-blue-500"
|
||||
>
|
||||
<view class="i-mdi-lock text-xl"></view>
|
||||
</view>
|
||||
<view class="field-control">
|
||||
<view class="field-icon i-mdi-lock" />
|
||||
<input
|
||||
v-model="loginForm.password"
|
||||
class="field-input"
|
||||
type="password"
|
||||
class="h-12 w-full rounded-xl bg-gray-50 pl-12 pr-4 text-sm text-gray-700 outline-none transition-all focus:bg-white focus:ring-2 focus:ring-blue-400"
|
||||
placeholder="请输入密码"
|
||||
placeholder="请输入您的密码"
|
||||
maxlength="20"
|
||||
confirm-type="done"
|
||||
@confirm="handleLogin"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- Captcha -->
|
||||
<view
|
||||
class="mb-8 flex items-center justify-between"
|
||||
v-if="captchaEnabled"
|
||||
>
|
||||
<view class="group relative mr-3 flex-1">
|
||||
<view
|
||||
class="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 transition-colors group-focus-within:text-blue-500"
|
||||
>
|
||||
<view class="i-mdi-security text-xl"></view>
|
||||
</view>
|
||||
<view v-if="captchaEnabled" class="captcha-row">
|
||||
<view class="field-control captcha-input">
|
||||
<view class="field-icon i-mdi-security" />
|
||||
<input
|
||||
v-model="loginForm.code"
|
||||
class="field-input"
|
||||
type="number"
|
||||
class="h-12 w-full rounded-xl bg-gray-50 pl-12 pr-4 text-sm text-gray-700 outline-none transition-all focus:bg-white focus:ring-2 focus:ring-blue-400"
|
||||
placeholder="验证码"
|
||||
placeholder="请输入验证码"
|
||||
maxlength="4"
|
||||
confirm-type="done"
|
||||
@confirm="handleLogin"
|
||||
/>
|
||||
</view>
|
||||
<view
|
||||
class="h-12 w-28 overflow-hidden rounded-xl bg-gray-100 shadow-sm transition-opacity active:opacity-80"
|
||||
@click="getCode"
|
||||
>
|
||||
<image :src="codeUrl" class="size-full object-cover"></image>
|
||||
<view class="captcha-image-wrap" @click="getCode">
|
||||
<image
|
||||
:src="codeUrl"
|
||||
class="captcha-image"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Login Button -->
|
||||
<button
|
||||
class="login-button"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
@click="handleLogin"
|
||||
class="flex h-12 w-full items-center justify-center rounded-xl bg-gradient-to-r from-blue-500 to-indigo-600 text-base font-semibold text-white shadow-lg shadow-blue-500/30 transition-transform active:scale-95"
|
||||
>
|
||||
登 录
|
||||
{{ loading ? "登录中..." : "登 录" }}
|
||||
</button>
|
||||
|
||||
<!-- Footer Links -->
|
||||
<view class="mt-6 flex flex-col items-center space-y-3">
|
||||
<view class="flex items-center text-sm text-gray-500" v-if="register">
|
||||
<text>没有账号?</text>
|
||||
<text
|
||||
@click="handleUserRegister"
|
||||
class="ml-1 font-medium text-blue-600 active:opacity-70"
|
||||
>立即注册</text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex flex-wrap items-center justify-center text-xs text-gray-400"
|
||||
>
|
||||
<view v-if="register" class="register-tip">
|
||||
<text>没有账号?</text>
|
||||
<text class="action-link" @click="handleUserRegister">立即注册</text>
|
||||
</view>
|
||||
|
||||
<view class="agreement-tip">
|
||||
<text>登录即代表同意</text>
|
||||
<text
|
||||
@click="handleUserAgrement"
|
||||
class="mx-1 text-blue-500 active:opacity-70"
|
||||
>《用户协议》</text
|
||||
>
|
||||
<text class="action-link" @click="handleUserAgrement">《用户协议》</text>
|
||||
<text>和</text>
|
||||
<text
|
||||
@click="handlePrivacy"
|
||||
class="mx-1 text-blue-500 active:opacity-70"
|
||||
>《隐私协议》</text
|
||||
>
|
||||
<text class="action-link" @click="handlePrivacy">《隐私协议》</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -129,13 +105,14 @@ import { useConfigStore, useUserStore } from "@/store";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const globalConfig = useConfigStore().config;
|
||||
const codeUrl = ref("");
|
||||
const loading = ref(false);
|
||||
// 验证码开关
|
||||
const captchaEnabled = ref(true);
|
||||
// 用户注册开关
|
||||
const register = ref(false);
|
||||
const loginForm = ref({
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
password: "",
|
||||
code: "",
|
||||
uuid: "",
|
||||
});
|
||||
@@ -160,6 +137,8 @@ function getCode() {
|
||||
getCodeImg().then((res) => {
|
||||
captchaEnabled.value =
|
||||
res.captchaEnabled === undefined ? true : res.captchaEnabled;
|
||||
register.value =
|
||||
res.registerEnabled === undefined ? false : res.registerEnabled;
|
||||
if (captchaEnabled.value) {
|
||||
codeUrl.value = "data:image/gif;base64," + res.img;
|
||||
loginForm.value.uuid = res.uuid;
|
||||
@@ -176,6 +155,7 @@ async function handleLogin() {
|
||||
} else if (loginForm.value.code === "" && captchaEnabled.value) {
|
||||
proxy.$modal.msgError("请输入验证码");
|
||||
} else {
|
||||
loading.value = true;
|
||||
proxy.$modal.loading("登录中,请耐心等待...");
|
||||
pwdLogin();
|
||||
}
|
||||
@@ -183,25 +163,26 @@ async function handleLogin() {
|
||||
|
||||
// 密码登录
|
||||
async function pwdLogin() {
|
||||
useUserStore()
|
||||
.login(loginForm.value)
|
||||
.then(() => {
|
||||
try {
|
||||
await useUserStore().login(loginForm.value);
|
||||
proxy.$modal.closeLoading();
|
||||
loading.value = false;
|
||||
loginSuccess();
|
||||
})
|
||||
.catch(() => {
|
||||
} catch (error) {
|
||||
proxy.$modal.closeLoading();
|
||||
loading.value = false;
|
||||
if (captchaEnabled.value) {
|
||||
getCode();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 登录成功后,处理函数
|
||||
function loginSuccess(result) {
|
||||
function loginSuccess() {
|
||||
// 设置用户信息
|
||||
useUserStore()
|
||||
.getInfo()
|
||||
.then((res) => {
|
||||
.then(() => {
|
||||
proxy.$tab.reLaunch("/pages/index");
|
||||
});
|
||||
}
|
||||
@@ -219,6 +200,239 @@ getCode();
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
background-color: #eef8f8;
|
||||
}
|
||||
|
||||
.login-page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 48rpx 32rpx;
|
||||
background: #eef8f8;
|
||||
}
|
||||
|
||||
.login-background,
|
||||
.login-scrim {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.login-background {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.login-scrim {
|
||||
z-index: 1;
|
||||
background: rgba(241, 250, 250, 0.22);
|
||||
}
|
||||
|
||||
.login-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 720rpx;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 100%;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.88);
|
||||
border-radius: 20rpx;
|
||||
padding: 56rpx 44rpx 42rpx;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 20rpx 60rpx rgba(40, 34, 92, 0.18);
|
||||
}
|
||||
|
||||
.brand-lockup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 46rpx;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.brand-logo-frame {
|
||||
display: flex;
|
||||
width: 400rpx;
|
||||
max-width: 100%;
|
||||
height: 136rpx;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 18rpx;
|
||||
padding: 16rpx 22rpx;
|
||||
background: #28225c;
|
||||
box-shadow: 0 10rpx 22rpx rgba(40, 34, 92, 0.16);
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
margin-top: 18rpx;
|
||||
color: #28225c;
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.brand-subtitle {
|
||||
margin-top: 10rpx;
|
||||
color: #6c7786;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.field-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 96rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 1rpx solid #d9e6e7;
|
||||
border-radius: 10rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #ffffff;
|
||||
transition: border-color 160ms ease, box-shadow 160ms ease;
|
||||
}
|
||||
|
||||
.field-control:focus-within {
|
||||
border-color: #008596;
|
||||
box-shadow: 0 0 0 4rpx rgba(0, 133, 150, 0.1);
|
||||
}
|
||||
|
||||
.field-icon {
|
||||
flex: 0 0 36rpx;
|
||||
margin-right: 18rpx;
|
||||
color: #008596;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 96rpx;
|
||||
color: #28333f;
|
||||
font-size: 28rpx;
|
||||
line-height: 96rpx;
|
||||
}
|
||||
|
||||
.captcha-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.captcha-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.captcha-image-wrap {
|
||||
flex: 0 0 220rpx;
|
||||
width: 220rpx;
|
||||
height: 96rpx;
|
||||
margin-bottom: 24rpx;
|
||||
overflow: hidden;
|
||||
border: 1rpx solid #d9e6e7;
|
||||
border-radius: 10rpx;
|
||||
background: #f4f8f8;
|
||||
}
|
||||
|
||||
.captcha-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
margin: 8rpx 0 0;
|
||||
border: 0;
|
||||
border-radius: 10rpx;
|
||||
background: #008596;
|
||||
box-shadow: 0 12rpx 24rpx rgba(0, 133, 150, 0.2);
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
line-height: 96rpx;
|
||||
transition: opacity 160ms ease, transform 160ms ease;
|
||||
}
|
||||
|
||||
.login-button::after {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.login-button:active {
|
||||
opacity: 0.84;
|
||||
transform: translateY(1rpx);
|
||||
}
|
||||
|
||||
.login-button[disabled] {
|
||||
opacity: 0.72;
|
||||
}
|
||||
|
||||
.register-tip,
|
||||
.agreement-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #7b8791;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.register-tip {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.agreement-tip {
|
||||
flex-wrap: wrap;
|
||||
margin-top: 22rpx;
|
||||
color: #9aa5ad;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.action-link {
|
||||
margin: 0 6rpx;
|
||||
color: #008596;
|
||||
}
|
||||
|
||||
.action-link:active {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 420px) {
|
||||
.login-page {
|
||||
padding: 32rpx 24rpx;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
padding: 44rpx 28rpx 34rpx;
|
||||
}
|
||||
|
||||
.brand-logo-frame {
|
||||
width: 360rpx;
|
||||
height: 122rpx;
|
||||
padding: 14rpx 20rpx;
|
||||
}
|
||||
|
||||
.captcha-image-wrap {
|
||||
flex-basis: 190rpx;
|
||||
width: 190rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
<view class="flex h-full flex-col bg-gray-50 pb-10">
|
||||
<!-- Header -->
|
||||
<view class="flex flex-col items-center py-10">
|
||||
<view class="flex h-24 w-64 items-center justify-center rounded-xl bg-gray-800 px-4 shadow-md">
|
||||
<image
|
||||
class="size-20 rounded-xl shadow-md"
|
||||
src="/static/logo200.png"
|
||||
mode="widthFix"
|
||||
class="h-20 w-full"
|
||||
src="/static/sylvan-logo.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="mt-4 text-xl font-bold text-gray-800"
|
||||
>智链云枢管理系统</text
|
||||
>
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<!-- Logo Section -->
|
||||
<view class="mb-6 flex flex-col items-center">
|
||||
<view
|
||||
class="mb-4 flex size-16 items-center justify-center rounded-2xl bg-white shadow-lg"
|
||||
class="mb-4 flex h-20 w-60 items-center justify-center rounded-2xl bg-gray-800 px-4 shadow-lg"
|
||||
>
|
||||
<image
|
||||
class="size-10"
|
||||
class="h-16 w-full"
|
||||
:src="globalConfig.appInfo.logo"
|
||||
mode="widthFix"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-xl font-bold tracking-wide text-gray-800"
|
||||
|
||||
@@ -1,199 +1,62 @@
|
||||
<template>
|
||||
<view class="flex h-full flex-col bg-gray-50 overflow-hidden">
|
||||
<scroll-view scroll-y class="flex-1" :show-scrollbar="false">
|
||||
<view class="p-4 pb-24 space-y-6">
|
||||
<!-- Banner -->
|
||||
<view
|
||||
class="relative h-40 w-full overflow-hidden rounded-2xl shadow-lg shadow-indigo-100"
|
||||
<view class="work-page">
|
||||
<scroll-view
|
||||
class="work-scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<swiper
|
||||
class="h-40 w-full"
|
||||
:current="swiperDotIndex"
|
||||
@change="changeSwiper"
|
||||
autoplay
|
||||
interval="3000"
|
||||
circular
|
||||
>
|
||||
<swiper-item v-for="(item, index) in data" :key="index">
|
||||
<view class="h-full w-full" @click="clickBannerItem(item)">
|
||||
<image
|
||||
:src="item.image"
|
||||
mode="scaleToFill"
|
||||
class="block h-full w-full"
|
||||
/>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- Custom Dots -->
|
||||
<view
|
||||
class="absolute bottom-3 right-0 left-0 flex justify-center space-x-2 pointer-events-none"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in data"
|
||||
:key="index"
|
||||
class="h-1.5 rounded-full transition-all duration-300"
|
||||
:class="current === index ? 'bg-white w-4' : 'bg-white/50 w-1.5'"
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- AI Tools Section -->
|
||||
<view>
|
||||
<view class="mb-4 flex items-center space-x-2">
|
||||
<view class="h-4 w-1 rounded-full bg-indigo-500"></view>
|
||||
<text class="text-base font-bold text-gray-800">AI 生产力</text>
|
||||
</view>
|
||||
<view class="grid grid-cols-2 gap-4">
|
||||
<view
|
||||
class="group relative overflow-hidden rounded-2xl bg-white p-5 shadow-sm transition-all active:scale-95"
|
||||
@click="handleToAiChat"
|
||||
>
|
||||
<view
|
||||
class="absolute -right-4 -top-4 size-20 rounded-full bg-indigo-50 opacity-50"
|
||||
></view>
|
||||
<view class="relative z-10 flex flex-col">
|
||||
<view
|
||||
class="mb-3 flex size-10 items-center justify-center rounded-xl bg-indigo-500 text-white shadow-md shadow-indigo-200"
|
||||
>
|
||||
<view class="i-mdi-chat text-xl"></view>
|
||||
</view>
|
||||
<text class="font-bold text-gray-800">智能对话</text>
|
||||
<text class="mt-1 text-xs text-gray-500">智能助手</text>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="group relative overflow-hidden rounded-2xl bg-white p-5 shadow-sm transition-all active:scale-95"
|
||||
@click="handleBuilding"
|
||||
>
|
||||
<view
|
||||
class="absolute -right-4 -top-4 size-20 rounded-full bg-purple-50 opacity-50"
|
||||
></view>
|
||||
<view class="relative z-10 flex flex-col">
|
||||
<view
|
||||
class="mb-3 flex size-10 items-center justify-center rounded-xl bg-purple-500 text-white shadow-md shadow-purple-200"
|
||||
>
|
||||
<view class="i-mdi-image-multiple text-xl"></view>
|
||||
</view>
|
||||
<text class="font-bold text-gray-800">图像生成</text>
|
||||
<text class="mt-1 text-xs text-gray-500">创意工坊</text>
|
||||
</view>
|
||||
<view class="work-content">
|
||||
<view class="work-heading">
|
||||
<view class="work-heading-copy">
|
||||
<text class="work-kicker">业务工作台</text>
|
||||
<text class="work-subtitle">
|
||||
根据当前账号权限,快速进入现场业务流程
|
||||
</text>
|
||||
</view>
|
||||
<view class="work-permission-count">
|
||||
<text class="work-permission-value">{{ visibleBusinessEntries.length }}</text>
|
||||
<text class="work-permission-label">项可用</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- System Management Section -->
|
||||
<view>
|
||||
<view class="mb-4 flex items-center space-x-2">
|
||||
<view class="h-4 w-1 rounded-full bg-blue-500"></view>
|
||||
<text class="text-base font-bold text-gray-800">系统管理</text>
|
||||
<TestBatchModeSwitch />
|
||||
|
||||
<view class="work-section">
|
||||
<view class="work-section-header">
|
||||
<view class="work-section-marker"></view>
|
||||
<view class="work-section-copy">
|
||||
<text class="work-section-title">业务管理</text>
|
||||
<text class="work-section-description">选择需要执行的现场流程</text>
|
||||
</view>
|
||||
<view class="rounded-2xl bg-white p-5 shadow-sm">
|
||||
<view class="flex flex-wrap justify-between gap-y-4">
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleToUserList"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-blue-50 text-blue-500 transition-colors group-active:bg-blue-100"
|
||||
>
|
||||
<view class="i-mdi-account text-xl"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600"
|
||||
>用户管理</text
|
||||
>
|
||||
</view>
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleBuilding"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-orange-50 text-orange-500 transition-colors group-active:bg-orange-100"
|
||||
>
|
||||
<view class="i-mdi-badge-account text-xl"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600"
|
||||
>角色管理</text
|
||||
>
|
||||
</view>
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleBuilding"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-green-50 text-green-500 transition-colors group-active:bg-green-100"
|
||||
>
|
||||
<view class="i-mdi-office-building text-xl"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600"
|
||||
>部门管理</text
|
||||
>
|
||||
</view>
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleBuilding"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-red-50 text-red-500 transition-colors group-active:bg-red-100"
|
||||
>
|
||||
<view class="i-mdi-cog text-xl"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600"
|
||||
>配置管理</text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view v-if="visibleBusinessEntries.length" class="work-entry-list">
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleBuilding"
|
||||
v-for="entry in visibleBusinessEntries"
|
||||
:key="entry.path"
|
||||
class="work-entry"
|
||||
@click="handleBusinessEntry(entry)"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-cyan-50 text-cyan-500 transition-colors group-active:bg-cyan-100"
|
||||
class="work-entry-icon"
|
||||
:class="entry.iconClass"
|
||||
>
|
||||
<view class="i-mdi-book-open-page-variant text-xl"></view>
|
||||
<view :class="entry.icon" class="work-entry-icon-glyph"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600"
|
||||
>字典管理</text
|
||||
>
|
||||
<view class="work-entry-copy">
|
||||
<text class="work-entry-title">{{ entry.title }}</text>
|
||||
<text class="work-entry-description">{{ entry.description }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleBuilding"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-yellow-50 text-yellow-500 transition-colors group-active:bg-yellow-100"
|
||||
>
|
||||
<view class="i-mdi-bullhorn text-xl"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600"
|
||||
>通知公告</text
|
||||
>
|
||||
</view>
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleBuilding"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-pink-50 text-pink-500 transition-colors group-active:bg-pink-100"
|
||||
>
|
||||
<view class="i-mdi-file-document-outline text-xl"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600"
|
||||
>日志管理</text
|
||||
>
|
||||
</view>
|
||||
<view
|
||||
class="w-[22%] flex flex-col items-center space-y-1.5 active:opacity-60"
|
||||
@click="handleBuilding"
|
||||
>
|
||||
<view
|
||||
class="flex size-10 items-center justify-center rounded-xl bg-gray-50 text-gray-500 transition-colors group-active:bg-gray-100"
|
||||
>
|
||||
<view class="i-mdi-dots-horizontal text-xl"></view>
|
||||
</view>
|
||||
<text class="text-[11px] font-medium text-gray-600">更多</text>
|
||||
<view class="i-mdi-chevron-right work-entry-arrow"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="work-empty-state">
|
||||
<view class="work-empty-icon">
|
||||
<view class="i-mdi-lock-outline work-empty-icon-glyph"></view>
|
||||
</view>
|
||||
<text class="work-empty-title">暂无已授权业务</text>
|
||||
<text class="work-empty-description">请联系管理员分配业务菜单权限</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -202,34 +65,291 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, getCurrentInstance } from "vue";
|
||||
import { computed, getCurrentInstance } from "vue";
|
||||
import TestBatchModeSwitch from "@/components/business/TestBatchModeSwitch.vue";
|
||||
import { businessMenuEntries } from "@/config/businessMenu";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const current = ref(0);
|
||||
const swiperDotIndex = ref(0);
|
||||
const data = ref([
|
||||
{ image: "/static/images/banner/banner01.jpg" },
|
||||
{ image: "/static/images/banner/banner02.jpg" },
|
||||
{ image: "/static/images/banner/banner03.jpg" },
|
||||
]);
|
||||
|
||||
function clickBannerItem(item) {
|
||||
console.info(item);
|
||||
}
|
||||
const visibleBusinessEntries = computed(() =>
|
||||
businessMenuEntries.filter((entry) => proxy.$auth.hasPermi(entry.permission)),
|
||||
);
|
||||
|
||||
function changeSwiper(e) {
|
||||
current.value = e.detail.current;
|
||||
}
|
||||
|
||||
function handleToAiChat() {
|
||||
handleBuilding();
|
||||
}
|
||||
|
||||
function handleToUserList() {
|
||||
handleBuilding();
|
||||
}
|
||||
|
||||
function handleBuilding() {
|
||||
proxy.$modal.msg("模块建设中~");
|
||||
function handleBusinessEntry(entry) {
|
||||
if (!proxy.$auth.hasPermi(entry.permission)) {
|
||||
proxy.$modal.msgError("当前账号没有该业务菜单权限");
|
||||
return;
|
||||
}
|
||||
proxy.$tab.navigateTo(entry.path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.work-page {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
background: #f4f8f8;
|
||||
}
|
||||
|
||||
.work-scroll {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.work-content {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 24rpx 28rpx calc(140rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.work-heading {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 18rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.work-heading-copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.work-kicker {
|
||||
display: block;
|
||||
color: #28225c;
|
||||
font-size: 38rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.work-subtitle {
|
||||
display: block;
|
||||
margin-top: 10rpx;
|
||||
color: #6c7786;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.work-permission-count {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: baseline;
|
||||
border-radius: 14rpx;
|
||||
padding: 12rpx 16rpx;
|
||||
background: #e9f7f6;
|
||||
color: #008596;
|
||||
flex-direction: column;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.work-permission-value {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.work-permission-label {
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.work-section {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
margin-top: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 8rpx 24rpx rgba(40, 34, 92, 0.06);
|
||||
}
|
||||
|
||||
.work-section-header {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: flex-start;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.work-section-marker {
|
||||
width: 8rpx;
|
||||
height: 38rpx;
|
||||
flex: 0 0 8rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #008596;
|
||||
}
|
||||
|
||||
.work-section-copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.work-section-title {
|
||||
display: block;
|
||||
color: #28225c;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.work-section-description {
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
color: #8b969d;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.work-entry-list {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
margin-top: 20rpx;
|
||||
gap: 12rpx;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.work-entry {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
border: 1rpx solid #e5eeee;
|
||||
border-radius: 16rpx;
|
||||
padding: 18rpx;
|
||||
background: #fbfdfd;
|
||||
}
|
||||
|
||||
.work-entry:active {
|
||||
border-color: #8ccbc5;
|
||||
background: #f0faf9;
|
||||
}
|
||||
|
||||
.work-entry-icon {
|
||||
display: flex;
|
||||
width: 82rpx;
|
||||
height: 82rpx;
|
||||
flex: 0 0 82rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 18rpx;
|
||||
}
|
||||
|
||||
.work-entry-icon-glyph {
|
||||
font-size: 42rpx;
|
||||
}
|
||||
|
||||
.work-entry-copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.work-entry-title,
|
||||
.work-entry-description {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.work-entry-title {
|
||||
color: #27313a;
|
||||
font-size: 27rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.work-entry-description {
|
||||
margin-top: 7rpx;
|
||||
color: #7b8791;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.work-entry-arrow {
|
||||
flex: 0 0 34rpx;
|
||||
color: #a9b7bb;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.work-empty-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 70rpx 16rpx 54rpx;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.work-empty-icon {
|
||||
display: flex;
|
||||
width: 84rpx;
|
||||
height: 84rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #f1f5f5;
|
||||
color: #9aa5ad;
|
||||
}
|
||||
|
||||
.work-empty-icon-glyph {
|
||||
font-size: 42rpx;
|
||||
}
|
||||
|
||||
.work-empty-title {
|
||||
display: block;
|
||||
margin-top: 20rpx;
|
||||
color: #4b5563;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.work-empty-description {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
color: #9aa5ad;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 420px) {
|
||||
.work-content {
|
||||
padding-right: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.work-kicker {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.work-section {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.work-entry {
|
||||
gap: 12rpx;
|
||||
padding: 14rpx;
|
||||
}
|
||||
|
||||
.work-entry-icon {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
flex-basis: 72rpx;
|
||||
}
|
||||
|
||||
.work-entry-icon-glyph {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 516.1 194.52">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #008596;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: #91cdb9;
|
||||
}
|
||||
|
||||
.cls-4 {
|
||||
fill: #43b69e;
|
||||
}
|
||||
|
||||
.cls-5 {
|
||||
fill: #554d8a;
|
||||
}
|
||||
|
||||
.cls-6 {
|
||||
fill: #50bcba;
|
||||
}
|
||||
|
||||
.cls-7, .cls-8, .cls-9 {
|
||||
fill: #28225c;
|
||||
}
|
||||
|
||||
.cls-10 {
|
||||
fill: #009a91;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.cls-10, .cls-11 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
.cls-11 {
|
||||
fill: #a5d7d7;
|
||||
}
|
||||
|
||||
.cls-8 {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.cls-9 {
|
||||
opacity: .25;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="tekening">
|
||||
<g>
|
||||
<g>
|
||||
<path class="cls-2" d="M302.33,61.89l-18.13,63.56c-3.35,11.65-7.77,17.59-23.63,17.59-3.99,0-10.36-.86-13.06-1.73-1.19-.43-2.27-.76-2.27-1.94v-8.74c0-1.19,.97-2.05,2.16-2.05h.22c3.56,.21,9.93,.86,13.49,.86,3.88,0,5.83-1.29,7.12-5.61l.97-3.35h-1.62c-3.99,0-7.34-.65-10.04-10.04l-14.24-48.56c-.11-.32-.11-.54-.11-.76,0-1.08,.65-1.73,2.16-1.73h14.35c1.08,0,1.83,.97,2.05,2.05l9.71,40.58c.32,1.51,.65,2.05,1.29,2.05h.97l10.04-42.63c.21-1.08,1.08-2.05,2.27-2.05h14.35c1.08,0,2.05,.86,2.05,1.94,0,.22-.11,.33-.11,.54Z"/>
|
||||
<path class="cls-2" d="M326.72,121.44h-13.17c-1.19,0-2.27-1.08-2.27-2.27V40.83c0-1.19,1.08-2.16,2.27-2.16h13.17c1.19,0,2.05,.97,2.05,2.16V119.17c0,1.19-.86,2.27-2.05,2.27Z"/>
|
||||
<path class="cls-2" d="M396,61.89l-13.6,48.99c-2.7,9.6-9.28,11.33-15.65,11.33s-12.84-1.73-15.54-11.33l-13.49-48.99c-.11-.21-.11-.43-.11-.65,0-1.08,.76-1.84,1.73-1.84h14.24c1.08,0,1.94,.97,2.27,2.27l9.28,42.3c.33,1.62,.86,2.27,1.62,2.27s1.4-.65,1.73-2.27l9.28-42.3c.32-1.3,1.19-2.27,2.27-2.27h14.24c.97,0,1.84,.76,1.84,1.84,0,.21,0,.43-.11,.65Z"/>
|
||||
<path class="cls-2" d="M418.66,121.3c-9.39,0-18.45-4.53-18.45-18.56v-.97c0-12.63,8.09-18.24,26.55-18.24h5.83v-3.89c0-6.15-6.78-7.47-11.85-7.47,0,0-17.96,1.95-17.96-12.69,0,0,16.18-.15,23.3-.15,15.54,0,23.99,4.55,23.99,20.31v39.49c0,1.19-.97,2.16-2.16,2.16h-29.24Zm13.89-27.41h-6.15c-7.56,0-9.5,2.59-9.5,7.88v.97c0,4.53,2.27,5.39,6.8,5.39,3.24,0,8.68,0,8.68,0l.17-14.25Z"/>
|
||||
<path class="cls-2" d="M497.01,58.61c15.65,0,19.1,10.9,19.1,24.5v35.93c0,1.19-1.08,2.27-2.27,2.27h-13.06c-1.19,0-2.16-1.08-2.16-2.27v-35.93c-.1-6.03,.45-8.65-6.13-8.65-3.22,0-8.33,.12-11.99,2.06v42.52c0,1.19-.97,2.27-2.16,2.27h-13.06c-1.19,0-2.27-1.08-2.27-2.27V61.63c0-1.19,.97-2.16,2.16-2.16l31.83-.86Z"/>
|
||||
<path class="cls-2" d="M184.25,121.47c0-17.9,16.39-18.14,16.39-18.14,8.24,0,17.54-.5,17.98-6.18,.24-3.16-.41-4.44-6.02-7.82l-15.23-8.96c-10.04-6.01-12.73-12.89-12.73-20.87,0-12.02,6.69-21.3,27.63-21.3,1.66,0,23.24,.05,23.26,.07-.13,.14,.12,2.04,.12,3.24,0,14.93-20.16,13.71-20.16,13.71-6.15,0-9.85,2.38-9.85,6.31,0,3.06,1.26,4.7,7.09,7.87l13.81,6.38c11.22,6.12,12.5,12.79,12.5,21.64,0,11.58-6.73,24.27-24.96,24.11l-29.81-.05Z"/>
|
||||
<g>
|
||||
<path class="cls-2" d="M365.04,163.65h-1.67c-.32,0-.58-.29-.58-.64v-22.95c0-.32,.23-.61,.58-.61h1.67c.32,0,.58,.26,.58,.61v22.95c0,.32-.23,.64-.58,.64Z"/>
|
||||
<path class="cls-2" d="M373.88,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
|
||||
<path class="cls-2" d="M393.77,146.72l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.34-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
|
||||
<path class="cls-2" d="M400.81,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
|
||||
<path class="cls-2" d="M420.03,163.65h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
|
||||
<path class="cls-2" d="M440.07,148.1l-1.16,.22c.58,.61,1.09,1.58,1.09,3.32v.26c0,3.54-2.19,5.5-7.15,5.5-1.71,0-2.8-.26-3.73-.64-.55,.26-.8,.61-.8,1.09,0,.55,.26,.84,1,1.03l4.8,1.22c3.15,.8,6.18,1.71,6.18,4.73v.26c0,3.09-2.06,4.99-7.34,4.99s-7.4-1.96-7.4-5.12v-.29c0-1.8,.97-3.28,2.03-4.09l-.23-.1c-1.03-.45-1.48-1.51-1.48-2.51v-.03c0-.87,.42-1.8,1.32-2.32-1-.93-1.48-2.09-1.48-3.73v-.26c0-2.86,1.61-5.7,7.14-5.7h7.05c.39,0,.74,.32,.74,.71v.84c0,.42-.23,.55-.58,.61Zm-2.61,16.74c0-1.26-1.03-1.77-3.93-2.51l-3.64-.93c-.93,.8-1.58,1.61-1.58,3.28v.29c0,1.8,1.06,2.93,4.63,2.93s4.51-1.03,4.51-2.8v-.26Zm-.26-13.2c0-1.93-.87-3.44-4.35-3.44s-4.34,1.64-4.34,3.44v.26c0,1.42,.64,3.28,4.34,3.28,3.48,0,4.35-1.35,4.35-3.28v-.26Z"/>
|
||||
<path class="cls-2" d="M365.07,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
|
||||
<path class="cls-2" d="M384.28,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
|
||||
<path class="cls-2" d="M403.17,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
|
||||
<path class="cls-2" d="M415.71,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
|
||||
<path class="cls-2" d="M440.52,177.36l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.35-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
|
||||
<path class="cls-2" d="M455.97,194.29h-1.48c-.35,0-.64-.29-.64-.64v-.71c-1.54,.9-3.15,1.58-4.96,1.58-2.74,0-5.25-1.19-5.25-5.21v-.39c0-3.48,2.03-5.21,7.76-5.21h2.38v-1.29c0-2.7-.93-3.64-3.44-3.64-2.09,0-3.64,.13-4.92,.26h-.16c-.29,0-.58-.1-.58-.51v-1.13c0-.35,.23-.55,.68-.61,1.32-.23,2.8-.42,4.99-.42,4.47,0,6.28,2.25,6.28,6.05v11.23c0,.35-.29,.64-.64,.64Zm-2.19-8.56h-2.38c-3.9,0-4.93,.87-4.93,3.15v.39c0,2.32,1.09,2.93,2.96,2.93,1.48,0,2.8-.45,4.35-1.29v-5.18Z"/>
|
||||
<path class="cls-2" d="M469.99,194.36c-.64,.1-1.48,.16-2.09,.16-2.96,0-4.38-.77-4.38-4.31v-11.23l-3.12-.35c-.35-.03-.61-.22-.61-.58v-.87c0-.35,.26-.58,.61-.58h3.12v-3.15c0-.32,.22-.58,.58-.64l1.67-.26h.1c.26,0,.48,.22,.48,.55v3.51h3.48c.35,0,.64,.29,.64,.64v1.09c0,.35-.29,.64-.64,.64h-3.48v11.23c0,1.77,.45,2.03,1.74,2.03h1.8c.45,0,.68,.13,.68,.45v1.13c0,.29-.16,.48-.58,.55Z"/>
|
||||
<path class="cls-2" d="M477.14,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
|
||||
<path class="cls-2" d="M490.01,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
|
||||
<path class="cls-2" d="M514.85,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-11" d="M57.78,67.44c-.03,5.02-.86,9.86-2.35,14.39,0,0,.01,.01,.02,.02,0,0,0,0,.01,0,10.82-31.09,38.25-54.41,71.67-59.3C130.83,15.86,132.95,8.18,132.95,0h0s-46.67,0-46.67,0H46.67C30.78,0,16.76,7.94,8.33,20.07c.88-.04,1.76-.07,2.65-.06,26.02,.17,46.98,21.41,46.8,47.43Z"/>
|
||||
<path class="cls-10" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
|
||||
<path class="cls-6" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
|
||||
<path class="cls-7" d="M55.46,81.85s-.01-.01-.02-.02c0,0,0,0,0,.01,0,0,.02,0,.02,0Z"/>
|
||||
<path class="cls-10" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
|
||||
<path class="cls-8" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
|
||||
<path class="cls-7" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
|
||||
<path class="cls-8" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
|
||||
<path class="cls-10" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
|
||||
<path class="cls-3" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
|
||||
<path class="cls-10" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
|
||||
<path class="cls-8" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
|
||||
<path class="cls-9" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
|
||||
<polygon class="cls-7" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
|
||||
<polygon class="cls-8" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
|
||||
<polygon class="cls-9" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
|
||||
<path class="cls-10" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
|
||||
<path class="cls-9" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
|
||||
<path class="cls-4" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
|
||||
<path class="cls-10" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
|
||||
<path class="cls-8" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
|
||||
<path class="cls-9" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
|
||||
<path class="cls-1" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
|
||||
<path class="cls-5" d="M55.47,81.86s0,0,0,0c-.02,0-.03,0-.05,.01,0-.01,0-.02,.01-.03,0,0,0,0,0-.01-9.09-10.29-21.81-17.47-36.5-19.35-5.49-.7-10.89-.6-16.11,.17,2.93,8.02,7.98,15.01,14.5,20.29,5.11,4.14,11.12,7.22,17.69,8.91,2.45,.63,4.96,1.08,7.55,1.3,.24,.02,.24,.34,0,.36-4.74,.41-9.26,1.54-13.49,3.27-8.71,3.55-16.1,9.65-21.25,17.38-4.93,7.4-7.82,16.28-7.82,25.84H0s8.74,0,8.74,0h57.85c1.51-3.77,2.58-7.74,3.11-11.86,2.23-17.44-3.45-34.06-14.23-46.28Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -1,9 +1,10 @@
|
||||
import { createPinia } from "pinia";
|
||||
import { useUserStore } from "./modules/user";
|
||||
import { useConfigStore } from "./modules/config";
|
||||
import { useTestBatchStore } from "./modules/testBatch";
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
export default pinia;
|
||||
|
||||
export { useUserStore, useConfigStore };
|
||||
export { useUserStore, useConfigStore, useTestBatchStore };
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { listAvailableTestBatches } from "@/api/system/testBatch";
|
||||
import storage from "@/utils/storage";
|
||||
import constant from "@/utils/constant";
|
||||
|
||||
export const useTestBatchStore = defineStore("testBatch", () => {
|
||||
const options = ref([]);
|
||||
const currentBatchId = ref(String(storage.get(constant.testBatchId) || ""));
|
||||
const currentBatchName = ref(storage.get(constant.testBatchName) || "");
|
||||
const scopeVersion = ref(0);
|
||||
const isTestMode = computed(() => Boolean(currentBatchId.value));
|
||||
|
||||
function leaveTestMode() {
|
||||
currentBatchId.value = "";
|
||||
currentBatchName.value = "";
|
||||
storage.remove(constant.testBatchId);
|
||||
storage.remove(constant.testBatchName);
|
||||
scopeVersion.value += 1;
|
||||
}
|
||||
|
||||
async function refreshOptions() {
|
||||
try {
|
||||
const response = await listAvailableTestBatches();
|
||||
const available = (Array.isArray(response.data) ? response.data : []).filter(
|
||||
(item) => item && item.status === "ACTIVE" && item.batchId != null,
|
||||
);
|
||||
options.value = available;
|
||||
|
||||
const current = available.find(
|
||||
(item) => String(item.batchId) === String(currentBatchId.value),
|
||||
);
|
||||
if (current) {
|
||||
currentBatchId.value = String(current.batchId);
|
||||
currentBatchName.value = current.batchName || "";
|
||||
storage.set(constant.testBatchId, currentBatchId.value);
|
||||
storage.set(constant.testBatchName, currentBatchName.value);
|
||||
} else if (currentBatchId.value) {
|
||||
// 本地缓存不能代替后端授权;选项接口未返回当前批次时立即回到正式模式。
|
||||
leaveTestMode();
|
||||
}
|
||||
return available;
|
||||
} catch (error) {
|
||||
options.value = [];
|
||||
leaveTestMode();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function selectBatch(batch) {
|
||||
const allowed = options.value.find(
|
||||
(item) =>
|
||||
String(item.batchId) === String(batch?.batchId) && item.status === "ACTIVE",
|
||||
);
|
||||
if (!allowed) {
|
||||
throw new Error("所选测试批次当前不可用");
|
||||
}
|
||||
currentBatchId.value = String(allowed.batchId);
|
||||
currentBatchName.value = allowed.batchName || "";
|
||||
storage.set(constant.testBatchId, currentBatchId.value);
|
||||
storage.set(constant.testBatchName, currentBatchName.value);
|
||||
scopeVersion.value += 1;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
options.value = [];
|
||||
leaveTestMode();
|
||||
}
|
||||
|
||||
return {
|
||||
options,
|
||||
currentBatchId,
|
||||
currentBatchName,
|
||||
scopeVersion,
|
||||
isTestMode,
|
||||
refreshOptions,
|
||||
selectBatch,
|
||||
leaveTestMode,
|
||||
reset,
|
||||
};
|
||||
});
|
||||
|
||||
export default useTestBatchStore;
|
||||
@@ -15,6 +15,7 @@ export const useUserStore = defineStore("user", () => {
|
||||
const id = ref(storage.get(constant.id));
|
||||
const name = ref(storage.get(constant.name));
|
||||
const avatar = ref(storage.get(constant.avatar));
|
||||
const admin = ref(storage.get(constant.admin) === true || storage.get(constant.admin) === "true");
|
||||
const roles = ref(storage.get(constant.roles));
|
||||
const permissions = ref(storage.get(constant.permissions));
|
||||
|
||||
@@ -33,6 +34,10 @@ export const useUserStore = defineStore("user", () => {
|
||||
avatar.value = val;
|
||||
storage.set(constant.avatar, val);
|
||||
};
|
||||
const SET_ADMIN = (val) => {
|
||||
admin.value = val === true;
|
||||
storage.set(constant.admin, admin.value);
|
||||
};
|
||||
const SET_ROLES = (val) => {
|
||||
roles.value = val;
|
||||
storage.set(constant.roles, val);
|
||||
@@ -81,6 +86,7 @@ export const useUserStore = defineStore("user", () => {
|
||||
} else {
|
||||
SET_ROLES(["ROLE_DEFAULT"]);
|
||||
}
|
||||
SET_ADMIN(user.admin === true);
|
||||
SET_ID(userid);
|
||||
SET_NAME(username);
|
||||
SET_AVATAR(avatar);
|
||||
@@ -115,6 +121,7 @@ export const useUserStore = defineStore("user", () => {
|
||||
id,
|
||||
name,
|
||||
avatar,
|
||||
admin,
|
||||
roles,
|
||||
permissions,
|
||||
SET_AVATAR,
|
||||
|
||||
@@ -2,8 +2,11 @@ const constant = {
|
||||
avatar: "user_avatar",
|
||||
id: "user_id",
|
||||
name: "user_name",
|
||||
admin: "user_admin",
|
||||
roles: "user_roles",
|
||||
permissions: "user_permissions",
|
||||
testBatchId: "test_batch_id",
|
||||
testBatchName: "test_batch_name",
|
||||
};
|
||||
|
||||
export default constant;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
export function normalizeScanValue(value) {
|
||||
const normalizedValue = String(value ?? "").trim();
|
||||
if (!normalizedValue) {
|
||||
throw new Error("二维码不能为空");
|
||||
}
|
||||
if (normalizedValue.length > 128) {
|
||||
throw new Error("二维码长度不能超过 128 个字符");
|
||||
}
|
||||
return normalizedValue;
|
||||
}
|
||||
|
||||
export function isDuplicateScan(
|
||||
value,
|
||||
lastValue,
|
||||
scannedAt,
|
||||
lastScannedAt,
|
||||
duplicateWindowMs,
|
||||
) {
|
||||
const elapsed = scannedAt - lastScannedAt;
|
||||
return (
|
||||
value === lastValue &&
|
||||
elapsed >= 0 &&
|
||||
elapsed <= duplicateWindowMs
|
||||
);
|
||||
}
|
||||
|
||||
export function createIdempotencyKey(
|
||||
cryptoProvider =
|
||||
typeof globalThis === "undefined" ? undefined : globalThis.crypto,
|
||||
) {
|
||||
if (typeof cryptoProvider?.randomUUID === "function") {
|
||||
return cryptoProvider.randomUUID();
|
||||
}
|
||||
if (typeof cryptoProvider?.getRandomValues !== "function") {
|
||||
throw new Error("当前环境不支持安全随机数,请使用 HTTPS 或 localhost 访问系统");
|
||||
}
|
||||
|
||||
const bytes = cryptoProvider.getRandomValues(new Uint8Array(16));
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||
const hex = Array.from(bytes, (byte) =>
|
||||
byte.toString(16).padStart(2, "0"),
|
||||
).join("");
|
||||
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import config from "@/config";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import errorCode from "@/utils/errorCode";
|
||||
import constant from "@/utils/constant";
|
||||
import storage from "@/utils/storage";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { toast, showConfirm, tansParams } from "@/utils/common";
|
||||
import {
|
||||
@@ -14,6 +16,37 @@ import {
|
||||
|
||||
let timeout = 10000;
|
||||
const baseUrl = config.baseUrl;
|
||||
let testBatchRecoveryStarted = false;
|
||||
|
||||
function getResponseBody(payload) {
|
||||
return payload?.data && typeof payload.data === "object" ? payload.data : payload || {};
|
||||
}
|
||||
|
||||
function getResponseData(payload) {
|
||||
const body = getResponseBody(payload);
|
||||
return body.data && typeof body.data === "object" ? body.data : body;
|
||||
}
|
||||
|
||||
function isTestBatchUnavailable(payload) {
|
||||
return getResponseData(payload)?.reason === "TEST_BATCH_UNAVAILABLE";
|
||||
}
|
||||
|
||||
function recoverUnavailableTestBatch(payload) {
|
||||
if (testBatchRecoveryStarted) return;
|
||||
testBatchRecoveryStarted = true;
|
||||
const body = getResponseBody(payload);
|
||||
const message = body.msg || "当前测试批次不可用,已切回正式数据模式";
|
||||
storage.remove(constant.testBatchId);
|
||||
storage.remove(constant.testBatchName);
|
||||
if (typeof uni.$emit === "function") {
|
||||
uni.$emit("test-batch-unavailable", message);
|
||||
}
|
||||
uni.showToast({ title: message, icon: "none", duration: 3500 });
|
||||
uni.reLaunch({ url: "/pages/work/index" });
|
||||
setTimeout(() => {
|
||||
testBatchRecoveryStarted = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const request = async (config) => {
|
||||
// 是否需要设置 token
|
||||
@@ -23,6 +56,10 @@ const request = async (config) => {
|
||||
if (getToken() && !isToken) {
|
||||
config.header["Authorization"] = "Bearer " + getToken();
|
||||
}
|
||||
const testBatchId = storage.get(constant.testBatchId);
|
||||
if (getToken() && testBatchId) {
|
||||
config.header["X-Test-Batch-Id"] = String(testBatchId);
|
||||
}
|
||||
|
||||
try {
|
||||
config = await encryptTransportRequest(config);
|
||||
@@ -55,8 +92,17 @@ const request = async (config) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const code = res.data.code || 200;
|
||||
const msg = errorCode[code] || res.data.msg || errorCode["default"];
|
||||
if (isTestBatchUnavailable(res)) {
|
||||
recoverUnavailableTestBatch(res);
|
||||
const error = new Error(getResponseBody(res).msg || "当前测试批次不可用");
|
||||
error.response = res;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const body = getResponseBody(res);
|
||||
const code = body.code || 200;
|
||||
const msg = errorCode[code] || body.msg || errorCode["default"];
|
||||
if (code === 401) {
|
||||
showConfirm(
|
||||
"登录状态已过期,您可以继续留在该页面,或者重新登录?",
|
||||
@@ -81,7 +127,7 @@ const request = async (config) => {
|
||||
error.response = res;
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(res.data);
|
||||
resolve(body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
@@ -100,10 +146,15 @@ const request = async (config) => {
|
||||
return request(config);
|
||||
}
|
||||
|
||||
if (isTestBatchUnavailable(error?.response)) {
|
||||
recoverUnavailableTestBatch(error.response);
|
||||
}
|
||||
|
||||
const response = error.response;
|
||||
const responseStatus = response?.status ?? response?.statusCode;
|
||||
const responseCode = response?.data?.code;
|
||||
const responseMsg = response?.data?.msg;
|
||||
const responseBody = getResponseBody(response);
|
||||
const responseCode = responseBody?.code;
|
||||
const responseMsg = responseBody?.msg;
|
||||
if (responseMsg) {
|
||||
uni.showToast({
|
||||
title: responseMsg,
|
||||
|
||||
@@ -8,8 +8,11 @@ let storageNodeKeys = [
|
||||
constant.avatar,
|
||||
constant.id,
|
||||
constant.name,
|
||||
constant.admin,
|
||||
constant.roles,
|
||||
constant.permissions,
|
||||
constant.testBatchId,
|
||||
constant.testBatchName,
|
||||
];
|
||||
|
||||
const storage = {
|
||||
|
||||
@@ -0,0 +1,407 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { test } from "node:test";
|
||||
|
||||
const appRoot = fileURLToPath(new URL("..", import.meta.url));
|
||||
|
||||
test("PDA 的 H5、App 和小程序统一使用 Sylvan 品牌标识", async () => {
|
||||
const [config, loginPage, registerPage, aboutPage, indexHtml, fullLogo, markPng] =
|
||||
await Promise.all([
|
||||
readFile(path.join(appRoot, "src/config.js"), "utf8"),
|
||||
readFile(path.join(appRoot, "src/pages/login.vue"), "utf8"),
|
||||
readFile(path.join(appRoot, "src/pages/register.vue"), "utf8"),
|
||||
readFile(path.join(appRoot, "src/pages/mine/about/index.vue"), "utf8"),
|
||||
readFile(path.join(appRoot, "index.html"), "utf8"),
|
||||
readFile(path.join(appRoot, "src/static/sylvan-logo.svg"), "utf8"),
|
||||
readFile(path.join(appRoot, "src/static/sylvan-mark.png")),
|
||||
]);
|
||||
|
||||
assert.match(config, /logo:\s*["']\/static\/sylvan-logo\.png["']/);
|
||||
assert.match(loginPage, /globalConfig\.appInfo\.logo/);
|
||||
assert.match(registerPage, /globalConfig\.appInfo\.logo/);
|
||||
assert.match(aboutPage, /src=["']\/static\/sylvan-logo\.png["']/);
|
||||
assert.match(indexHtml, /href=["']\/static\/favicon\.ico["']/);
|
||||
assert.match(fullLogo, /viewBox="0 0 516\.1 194\.52"/);
|
||||
assert.equal(markPng.subarray(1, 4).toString("ascii"), "PNG");
|
||||
assert.doesNotMatch(`${config}\n${aboutPage}`, /\/static\/logo(?:200)?\.png/);
|
||||
assert.doesNotMatch(fullLogo, /<script\b|<foreignObject\b|javascript:|\b(?:xlink:)?href=|\bonload=|\bonerror=/i);
|
||||
});
|
||||
|
||||
const apiContracts = {
|
||||
"src/api/business/inbound.js": [
|
||||
["listInbound", "'/business/inbound/list'", "get"],
|
||||
["getInboundWarehouseOptions", "'/business/inbound/warehouse-options'", "get"],
|
||||
["scanInboundPallet", "'/business/inbound/scan/' + qrToken", "get"],
|
||||
["getInbound", "'/business/inbound/' + inboundId", "get"],
|
||||
["addInbound", "'/business/inbound'", "post"],
|
||||
["updateInbound", "'/business/inbound'", "put"],
|
||||
["confirmInbound", "'/business/inbound/' + inboundId + '/confirm'", "put"],
|
||||
],
|
||||
"src/api/business/filling.js": [
|
||||
["listFilling", "'/business/filling/list'", "get"],
|
||||
["getFillingStrainOptions", "'/business/filling/strain-options'", "get"],
|
||||
["scanFillingPallet", "'/business/filling/scan/' + qrToken", "get"],
|
||||
["getFilling", "'/business/filling/' + fillingId", "get"],
|
||||
["addFilling", "'/business/filling'", "post"],
|
||||
],
|
||||
"src/api/business/shipment.js": [
|
||||
["listShipment", "'/business/shipment/list'", "get"],
|
||||
["getShipmentWarehouseOptions", "'/business/shipment/warehouse-options'", "get"],
|
||||
["getShipmentSiteOptions", "'/business/shipment/site-options'", "get"],
|
||||
["scanShipmentPallet", "'/business/shipment/scan/' + qrToken", "get"],
|
||||
["getShipment", "'/business/shipment/' + shipmentId", "get"],
|
||||
["addShipment", "'/business/shipment'", "post"],
|
||||
["updateShipment", "'/business/shipment'", "put"],
|
||||
["confirmShipment", "'/business/shipment/' + shipmentId + '/confirm'", "put"],
|
||||
],
|
||||
"src/api/business/usage.js": [
|
||||
["listUsage", "'/business/usage/list'", "get"],
|
||||
["getUsageLineOptions", "'/business/usage/line-options'", "get"],
|
||||
["scanUsagePallet", "'/business/usage/scan/' + qrToken", "get"],
|
||||
["getUsage", "'/business/usage/' + usageId", "get"],
|
||||
["addUsage", "'/business/usage'", "post"],
|
||||
],
|
||||
"src/api/business/return.js": [
|
||||
["listReturn", "'/business/return/list'", "get"],
|
||||
["getReturnWarehouseOptions", "'/business/return/warehouse-options'", "get"],
|
||||
["scanReturnPallet", "'/business/return/scan/' + qrToken", "get"],
|
||||
["requestReturn", "'/business/return/request'", "post"],
|
||||
["getReturn", "'/business/return/' + returnId", "get"],
|
||||
["confirmReturn", "'/business/return/' + returnId + '/confirm'", "put"],
|
||||
],
|
||||
};
|
||||
|
||||
function sourceForFunction(source, functionName) {
|
||||
const match = source.match(
|
||||
new RegExp(`export function ${escapeRegExp(functionName)}\\s*\\(`),
|
||||
);
|
||||
const start = match?.index ?? -1;
|
||||
assert.notEqual(start, -1, `${functionName} 导出不存在`);
|
||||
const end = source.indexOf("\n}", start);
|
||||
return source.slice(start, end === -1 ? source.length : end + 2);
|
||||
}
|
||||
|
||||
test("五个 PDA API 模块使用现有业务路径和 HTTP 方法", async (t) => {
|
||||
for (const [relativePath, contracts] of Object.entries(apiContracts)) {
|
||||
await t.test(relativePath, async () => {
|
||||
const source = await readFile(path.join(appRoot, relativePath), "utf8");
|
||||
for (const [functionName, url, method] of contracts) {
|
||||
const functionSource = sourceForFunction(source, functionName);
|
||||
const normalizedFunctionSource = functionSource.replaceAll("'", '"');
|
||||
const normalizedUrl = url.replaceAll("'", '"');
|
||||
assert.match(
|
||||
normalizedFunctionSource,
|
||||
new RegExp(`url:\\s*${escapeRegExp(normalizedUrl)}`),
|
||||
);
|
||||
assert.match(functionSource, new RegExp(`method:\\s*["']${method}["']`));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("扫码值只去除首尾空白并拒绝空值", async () => {
|
||||
const { normalizeScanValue } = await importSource("src/utils/palletScan.js");
|
||||
assert.equal(normalizeScanValue(" QR-001\n"), "QR-001");
|
||||
assert.throws(() => normalizeScanValue(" \n\t "), /二维码不能为空/);
|
||||
});
|
||||
|
||||
test("同一扫码值只在短时窗口内被识别为重复", async () => {
|
||||
const { isDuplicateScan } = await importSource("src/utils/palletScan.js");
|
||||
assert.equal(isDuplicateScan("QR-001", "QR-001", 1200, 1000, 500), true);
|
||||
assert.equal(isDuplicateScan("QR-001", "QR-002", 1200, 1000, 500), false);
|
||||
assert.equal(isDuplicateScan("QR-001", "QR-001", 1801, 1000, 500), false);
|
||||
});
|
||||
|
||||
test("幂等键优先使用 randomUUID,回退时使用 UUID v4 安全随机字节", async () => {
|
||||
const { createIdempotencyKey } = await importSource("src/utils/palletScan.js");
|
||||
const expected = "12345678-1234-4123-8123-123456789abc";
|
||||
assert.equal(createIdempotencyKey({ randomUUID: () => expected }), expected);
|
||||
assert.equal(
|
||||
createIdempotencyKey({
|
||||
getRandomValues(bytes) {
|
||||
bytes.fill(0);
|
||||
return bytes;
|
||||
},
|
||||
}),
|
||||
"00000000-0000-4000-8000-000000000000",
|
||||
);
|
||||
});
|
||||
|
||||
test("PalletScanInput 使用 HID 回车提交并由 loading 锁定输入", async () => {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, "src/components/business/PalletScanInput.vue"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(source, /@confirm=["']handleSubmit["']/);
|
||||
assert.match(source, /normalizeScanValue/);
|
||||
assert.match(source, /isDuplicateScan/);
|
||||
assert.match(source, /emit\(["']scan["']/);
|
||||
assert.match(source, /emit\(["']invalid["']/);
|
||||
assert.match(source, /emit\(["']duplicate["']/);
|
||||
assert.match(source, /loading/);
|
||||
assert.doesNotMatch(source, /uni\.scanCode|camera|摄像头/);
|
||||
});
|
||||
|
||||
test("PDA 路由和工作台入口覆盖五类现场业务", async () => {
|
||||
const pages = JSON.parse(
|
||||
await readFile(path.join(appRoot, "src/pages.json"), "utf8"),
|
||||
);
|
||||
const pagePaths = new Set(pages.pages.map((page) => page.path));
|
||||
for (const pagePath of [
|
||||
"pages/business/inbound/index",
|
||||
"pages/business/filling/index",
|
||||
"pages/business/shipment/index",
|
||||
"pages/business/usage/index",
|
||||
"pages/business/return/index",
|
||||
]) {
|
||||
assert.equal(pagePaths.has(pagePath), true, `${pagePath} 路由缺失`);
|
||||
}
|
||||
|
||||
const [workSource, menuSource] = await Promise.all([
|
||||
readFile(path.join(appRoot, "src/pages/work/index.vue"), "utf8"),
|
||||
readFile(path.join(appRoot, "src/config/businessMenu.js"), "utf8"),
|
||||
]);
|
||||
assert.match(workSource, /hasPermi\(entry\.permission\)/);
|
||||
assert.match(menuSource, /business:inbound:list/);
|
||||
assert.match(menuSource, /business:filling:list/);
|
||||
assert.match(menuSource, /business:shipment:list/);
|
||||
assert.match(menuSource, /business:usage:list/);
|
||||
assert.match(menuSource, /business:return:list/);
|
||||
assert.match(menuSource, /pages\/business\/inbound\/index/);
|
||||
assert.match(menuSource, /pages\/business\/filling\/index/);
|
||||
assert.match(menuSource, /pages\/business\/shipment\/index/);
|
||||
assert.match(menuSource, /pages\/business\/usage\/index/);
|
||||
assert.match(menuSource, /pages\/business\/return\/index/);
|
||||
});
|
||||
|
||||
test("首页展示真实运营驾驶舱,不保留模板入口和虚假统计", async () => {
|
||||
const source = await readFile(path.join(appRoot, "src/pages/index.vue"), "utf8");
|
||||
assert.match(source, /listScanExceptions/);
|
||||
assert.match(source, /最近扫码异常/);
|
||||
assert.match(source, /TestBatchBanner/);
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/快捷入口|新闻|计划|请假申请|周报|工作效率|新通知|待办任务|85%/,
|
||||
);
|
||||
});
|
||||
|
||||
test("工作台只展示真实业务入口并使用后端列表权限", async () => {
|
||||
const [workSource, menuModule] = await Promise.all([
|
||||
readFile(path.join(appRoot, "src/pages/work/index.vue"), "utf8"),
|
||||
importSource("src/config/businessMenu.js"),
|
||||
]);
|
||||
|
||||
assert.deepEqual(
|
||||
menuModule.businessMenuEntries.map(({ title, permission, path: entryPath }) => ({
|
||||
title,
|
||||
permission,
|
||||
path: entryPath,
|
||||
})),
|
||||
[
|
||||
{
|
||||
title: "仓库入库",
|
||||
permission: "business:inbound:list",
|
||||
path: "/pages/business/inbound/index",
|
||||
},
|
||||
{
|
||||
title: "工厂装填",
|
||||
permission: "business:filling:list",
|
||||
path: "/pages/business/filling/index",
|
||||
},
|
||||
{
|
||||
title: "发货出库",
|
||||
permission: "business:shipment:list",
|
||||
path: "/pages/business/shipment/index",
|
||||
},
|
||||
{
|
||||
title: "客户现场使用",
|
||||
permission: "business:usage:list",
|
||||
path: "/pages/business/usage/index",
|
||||
},
|
||||
{
|
||||
title: "退回仓库",
|
||||
permission: "business:return:list",
|
||||
path: "/pages/business/return/index",
|
||||
},
|
||||
],
|
||||
);
|
||||
assert.match(workSource, /businessMenuEntries/);
|
||||
assert.match(workSource, /hasPermi\(entry\.permission\)/);
|
||||
assert.match(workSource, /navigateTo\(entry\.path\)/);
|
||||
assert.doesNotMatch(workSource, /AI 生产力|智能对话|图像生成|系统管理|handleBuilding|模块建设中/);
|
||||
});
|
||||
|
||||
test("运营驾驶舱 API 对接现有扫码异常接口", async () => {
|
||||
const source = await readFile(path.join(appRoot, "src/api/business/dashboard.js"), "utf8");
|
||||
assert.match(source, /export function listScanExceptions\(/);
|
||||
assert.match(source, /url:\s*["']\/business\/dashboard\/scan-exceptions["']/);
|
||||
assert.match(source, /method:\s*["']get["']/);
|
||||
});
|
||||
|
||||
test("APP 页面标题与后端业务菜单名称一致", async () => {
|
||||
const [pages, fillingSource] = await Promise.all([
|
||||
JSON.parse(await readFile(path.join(appRoot, "src/pages.json"), "utf8")),
|
||||
readFile(path.join(appRoot, "src/pages/business/filling/index.vue"), "utf8"),
|
||||
]);
|
||||
const fillingPage = pages.pages.find((page) => page.path === "pages/business/filling/index");
|
||||
assert.equal(fillingPage?.style?.navigationBarTitleText, "工厂装填");
|
||||
assert.match(fillingSource, />工厂装填</);
|
||||
});
|
||||
|
||||
test("PDA 测试模式附加批次请求头并在五个业务页持续提示", async () => {
|
||||
const requestSource = await readFile(
|
||||
path.join(appRoot, "src/utils/request.js"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(requestSource, /X-Test-Batch-Id/);
|
||||
assert.match(requestSource, /TEST_BATCH_UNAVAILABLE/);
|
||||
assert.match(requestSource, /uni\.\$emit\(["']test-batch-unavailable["']/);
|
||||
assert.match(requestSource, /reLaunch\(\{\s*url:\s*["']\/pages\/work\/index["']/);
|
||||
|
||||
for (const page of ["inbound", "filling", "shipment", "usage", "return"]) {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, `src/pages/business/${page}/index.vue`),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(source, /TestBatchBanner/);
|
||||
}
|
||||
});
|
||||
|
||||
test("PDA 不再暴露入库和发货草稿物理删除", async () => {
|
||||
for (const file of ["src/api/business/inbound.js", "src/api/business/shipment.js"]) {
|
||||
const source = await readFile(path.join(appRoot, file), "utf8");
|
||||
assert.doesNotMatch(source, /delInbound|delShipment|method:\s*["']delete["']/);
|
||||
}
|
||||
for (const page of ["inbound", "shipment"]) {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, `src/pages/business/${page}/index.vue`),
|
||||
"utf8",
|
||||
);
|
||||
assert.doesNotMatch(source, /deleteDraft|deleteLoading|business:(?:inbound|shipment):remove/);
|
||||
}
|
||||
});
|
||||
|
||||
test("登录页只预填账号,不把管理员密码写入源码", async () => {
|
||||
const source = await readFile(path.join(appRoot, "src/pages/login.vue"), "utf8");
|
||||
assert.match(source, /username:\s*["']admin["']/);
|
||||
assert.match(source, /password:\s*["']["']/);
|
||||
assert.doesNotMatch(source, /password:\s*["'][^"']+["']/);
|
||||
});
|
||||
|
||||
test("PDA 后端地址支持 VITE_APP_BASE_API 并保留本地默认值", async () => {
|
||||
const source = await readFile(path.join(appRoot, "src/config.js"), "utf8");
|
||||
assert.match(source, /import\.meta\.env\.VITE_APP_BASE_API/);
|
||||
assert.match(source, /VITE_APP_BASE_API\s*\|\|\s*["']http:\/\/localhost:9099["']/);
|
||||
});
|
||||
|
||||
test("入库页面遵循 UNSTOCKED 扫码、草稿和幂等确认契约", async () => {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, "src/pages/business/inbound/index.vue"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(source, /PalletScanInput/);
|
||||
assert.match(source, /scanInboundPallet/);
|
||||
assert.match(source, /UNSTOCKED/);
|
||||
assert.match(source, /warehouseId/);
|
||||
assert.match(source, /palletIds/);
|
||||
assert.match(source, /getInbound\(/);
|
||||
assert.match(source, /updateInbound/);
|
||||
assert.match(source, /confirmInbound/);
|
||||
assert.match(source, /idempotencyKey/);
|
||||
assert.match(source, /business:inbound:(?:query|add|edit|confirm)/);
|
||||
});
|
||||
|
||||
test("发货页面遵循来源仓库、目标现场和整单确认契约", async () => {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, "src/pages/business/shipment/index.vue"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(source, /PalletScanInput/);
|
||||
assert.match(source, /scanShipmentPallet/);
|
||||
assert.match(source, /FILLED_IN_WAREHOUSE/);
|
||||
assert.match(source, /remainingPackages/);
|
||||
assert.match(source, /sourceWarehouseId/);
|
||||
assert.match(source, /targetSiteId/);
|
||||
assert.match(source, /palletIds/);
|
||||
assert.match(source, /getShipment\(/);
|
||||
assert.match(source, /updateShipment/);
|
||||
assert.match(source, /confirmShipment/);
|
||||
assert.match(source, /idempotencyKey/);
|
||||
assert.match(source, /business:shipment:(?:query|add|edit|confirm)/);
|
||||
});
|
||||
|
||||
test("装填页面遵循空托盘状态和 FillingCreateModel 字段契约", async () => {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, "src/pages/business/filling/index.vue"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(source, /PalletScanInput/);
|
||||
assert.match(source, /scanFillingPallet/);
|
||||
assert.match(source, /EMPTY_IN_WAREHOUSE/);
|
||||
for (const field of [
|
||||
"qrToken",
|
||||
"warehouseId",
|
||||
"strainId",
|
||||
"batchNo",
|
||||
"packageSpec",
|
||||
"initialPackages",
|
||||
"idempotencyKey",
|
||||
]) {
|
||||
assert.match(source, new RegExp(field));
|
||||
}
|
||||
assert.match(source, /addFilling/);
|
||||
assert.match(source, /business:filling:(?:query|add)/);
|
||||
});
|
||||
|
||||
test("现场使用页面遵循产线绑定、余量和使用结果契约", async () => {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, "src/pages/business/usage/index.vue"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(source, /PalletScanInput/);
|
||||
assert.match(source, /scanUsagePallet/);
|
||||
assert.match(source, /AT_SITE_UNUSED/);
|
||||
assert.match(source, /AT_SITE_PARTIAL/);
|
||||
assert.match(source, /currentLineId/);
|
||||
assert.match(source, /getUsageLineOptions/);
|
||||
assert.match(source, /usedPackages/);
|
||||
assert.match(source, /remainingPackages/);
|
||||
assert.match(source, /flowState/);
|
||||
assert.match(source, /addUsage/);
|
||||
assert.match(source, /idempotencyKey/);
|
||||
assert.match(source, /business:usage:(?:query|add)/);
|
||||
});
|
||||
|
||||
test("退回页面实现申请与确认两阶段的集合和幂等契约", async () => {
|
||||
const source = await readFile(
|
||||
path.join(appRoot, "src/pages/business/return/index.vue"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(source, /PalletScanInput/);
|
||||
assert.match(source, /scanReturnPallet/);
|
||||
assert.match(source, /mode:\s*["']request["']/);
|
||||
assert.match(source, /mode:\s*["']confirm["']/);
|
||||
assert.match(source, /sourceSiteId/);
|
||||
assert.match(source, /targetWarehouseId/);
|
||||
assert.match(source, /palletIds/);
|
||||
assert.match(source, /scannedPalletIds/);
|
||||
assert.match(source, /requestReturn/);
|
||||
assert.match(source, /getReturn\(/);
|
||||
assert.match(source, /confirmReturn/);
|
||||
assert.match(source, /sameIdSet/);
|
||||
assert.match(source, /REQUESTED/);
|
||||
assert.match(source, /requestIdempotencyKey/);
|
||||
assert.match(source, /confirmIdempotencyKey/);
|
||||
assert.match(source, /business:return:(?:query|request|confirm)/);
|
||||
});
|
||||
|
||||
function escapeRegExp(value) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
async function importSource(relativePath) {
|
||||
const source = await readFile(path.join(appRoot, relativePath), "utf8");
|
||||
return import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}`);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import test from "node:test";
|
||||
|
||||
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const readSource = (relativePath) =>
|
||||
readFileSync(resolve(appRoot, relativePath), "utf8");
|
||||
|
||||
test("APP 首页展示 PC 端核心运营统计并保护移动端横向空间", () => {
|
||||
const source = readSource("src/pages/index.vue");
|
||||
|
||||
for (const className of [
|
||||
"dashboard-overview",
|
||||
"dashboard-kpi-grid",
|
||||
"dashboard-trend-card",
|
||||
"dashboard-distribution-card",
|
||||
"dashboard-trend-range",
|
||||
"dashboard-status-list",
|
||||
]) {
|
||||
assert.match(source, new RegExp(`class=\\"[^\\"]*${className}[^\\"]*\\"`));
|
||||
}
|
||||
|
||||
for (const label of [
|
||||
"当前库存托盘",
|
||||
"剩余包数",
|
||||
"今日入库单",
|
||||
"今日出库单",
|
||||
"待确认单据",
|
||||
"入库 / 出库趋势",
|
||||
"托盘流转状态分布",
|
||||
]) {
|
||||
assert.match(source, new RegExp(label));
|
||||
}
|
||||
|
||||
assert.match(source, /listPalletInventory/);
|
||||
assert.match(source, /listShipmentReport/);
|
||||
assert.match(source, /listInbound/);
|
||||
assert.match(source, /listShipment/);
|
||||
assert.match(source, /listReturn/);
|
||||
assert.match(source, /TREND_RANGE_OPTIONS/);
|
||||
assert.match(source, /overflow-x:\s*hidden/);
|
||||
assert.match(source, /min-width:\s*0/);
|
||||
});
|
||||
|
||||
test("APP 首页统计保留加载、空数据和部分失败状态", () => {
|
||||
const source = readSource("src/pages/index.vue");
|
||||
|
||||
assert.match(source, /statsLoading/);
|
||||
assert.match(source, /statsError/);
|
||||
assert.match(source, /dashboard-stat-empty/);
|
||||
assert.match(source, /dashboard-stat-error/);
|
||||
assert.match(source, /formatMetric/);
|
||||
assert.match(source, /--/);
|
||||
});
|
||||
|
||||
test("APP 首页扫码异常支持默认收起并通过标题展开", () => {
|
||||
const source = readSource("src/pages/index.vue");
|
||||
|
||||
assert.match(source, /const exceptionsExpanded = ref\(false\)/);
|
||||
assert.match(source, /function toggleExceptionList\(\)/);
|
||||
assert.match(source, /class="[^"]*exception-header[^"]*"/);
|
||||
assert.match(source, /@click="toggleExceptionList"/);
|
||||
assert.match(source, /exception-toggle-icon/);
|
||||
assert.match(source, /v-else-if="exceptionsExpanded" class="exception-list"/);
|
||||
});
|
||||
|
||||
test("APP 首页报表 API 复用现有只读报表路径", () => {
|
||||
const inventorySource = readSource("src/api/report/palletInventory.js");
|
||||
const shipmentSource = readSource("src/api/report/shipment.js");
|
||||
|
||||
assert.match(inventorySource, /export function listPalletInventory\(/);
|
||||
assert.match(inventorySource, /url:\s*["']\/report\/pallet-inventory\/list["']/);
|
||||
assert.match(inventorySource, /method:\s*["']get["']/);
|
||||
assert.match(shipmentSource, /export function listShipmentReport\(/);
|
||||
assert.match(shipmentSource, /url:\s*["']\/report\/shipment\/list["']/);
|
||||
assert.match(shipmentSource, /method:\s*["']get["']/);
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import test from "node:test";
|
||||
|
||||
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const loginPagePath = resolve(appRoot, "src/pages/login.vue");
|
||||
const loginPageSource = readFileSync(loginPagePath, "utf8");
|
||||
|
||||
test("APP 登录页沿用 PC 端的品牌背景与登录卡片结构", () => {
|
||||
assert.match(loginPageSource, /class="login-page"/);
|
||||
assert.match(loginPageSource, /\/static\/login-background\.jpg/);
|
||||
assert.match(loginPageSource, /class="login-card"/);
|
||||
assert.match(loginPageSource, /globalConfig\.appInfo\.name/);
|
||||
assert.doesNotMatch(
|
||||
loginPageSource,
|
||||
/bg-gradient-to-br from-blue-50 to-indigo-100/,
|
||||
);
|
||||
assert.ok(
|
||||
existsSync(resolve(appRoot, "src/static/login-background.jpg")),
|
||||
"APP 登录页需要包含与 PC 端一致的背景资源",
|
||||
);
|
||||
});
|
||||
|
||||
test("APP 登录页保留验证码刷新与回车登录交互", () => {
|
||||
assert.match(loginPageSource, /@click="getCode"/);
|
||||
assert.match(loginPageSource, /@confirm="handleLogin"/);
|
||||
assert.match(loginPageSource, /captchaEnabled/);
|
||||
});
|
||||
|
||||
test("APP 登录页为白色 Sylvan 字标提供深色承载层", () => {
|
||||
assert.match(loginPageSource, /class="brand-logo-frame"/);
|
||||
assert.match(
|
||||
loginPageSource,
|
||||
/\.brand-logo-frame\s*\{[\s\S]*background:\s*#28225c/,
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import test from "node:test";
|
||||
|
||||
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const readSource = (relativePath) =>
|
||||
readFileSync(resolve(appRoot, relativePath), "utf8");
|
||||
|
||||
const homeSource = readSource("src/pages/index.vue");
|
||||
const workSource = readSource("src/pages/work/index.vue");
|
||||
const bannerSource = readSource("src/components/business/TestBatchBanner.vue");
|
||||
const modeSwitchSource = readSource(
|
||||
"src/components/business/TestBatchModeSwitch.vue",
|
||||
);
|
||||
|
||||
test("首页使用受约束的运营布局并保护横向空间", () => {
|
||||
assert.match(homeSource, /class="dashboard-page"/);
|
||||
assert.match(homeSource, /class="dashboard-scroll"/);
|
||||
assert.match(homeSource, /class="dashboard-content"/);
|
||||
assert.match(homeSource, /class="dashboard-hero"/);
|
||||
assert.match(homeSource, /class="dashboard-metric-grid"/);
|
||||
assert.match(homeSource, /class="exception-list"/);
|
||||
assert.match(homeSource, /class="exception-item"/);
|
||||
assert.match(homeSource, /overflow-x:\s*hidden/);
|
||||
assert.match(homeSource, /overflow-wrap:\s*anywhere/);
|
||||
});
|
||||
|
||||
test("工作台入口在窄屏下保持单列和可换行", () => {
|
||||
assert.match(workSource, /class="work-page"/);
|
||||
assert.match(workSource, /class="work-scroll"/);
|
||||
assert.match(workSource, /class="work-content"/);
|
||||
assert.match(workSource, /class="work-entry-list"/);
|
||||
assert.match(workSource, /class="work-entry"/);
|
||||
assert.match(workSource, /class="work-entry-copy"/);
|
||||
assert.match(workSource, /overflow-x:\s*hidden/);
|
||||
assert.match(workSource, /overflow-wrap:\s*anywhere/);
|
||||
});
|
||||
|
||||
test("测试批次提示和切换区不会撑开页面", () => {
|
||||
assert.match(bannerSource, /class="test-batch-banner"/);
|
||||
assert.match(bannerSource, /overflow-wrap:\s*anywhere/);
|
||||
assert.match(modeSwitchSource, /class="test-batch-switch"/);
|
||||
assert.match(modeSwitchSource, /class="test-batch-picker"/);
|
||||
assert.match(modeSwitchSource, /overflow-wrap:\s*anywhere/);
|
||||
});
|
||||
@@ -42,7 +42,7 @@ JWT_REDIS_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
# -------- 数据库配置 --------
|
||||
# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
|
||||
# 数据库类型,固定使用'mysql'
|
||||
DB_TYPE = 'mysql'
|
||||
# 数据库主机
|
||||
DB_HOST = '127.0.0.1'
|
||||
@@ -171,3 +171,5 @@ TRANSPORT_CRYPTO_ENABLED_PATHS = ''
|
||||
TRANSPORT_CRYPTO_REQUIRED_PATHS = ''
|
||||
# 排除传输层加密的路径列表,多个值使用逗号分隔
|
||||
TRANSPORT_CRYPTO_EXCLUDE_PATHS = '/openapi.json,/docs,/docs/oauth2-redirect,/redoc,/transport/crypto/frontend-config,/transport/crypto/public-key,/common/download,/common/download/resource'
|
||||
# 测试批次在线清理最大数据行数;0 表示禁止在线清理
|
||||
TEST_BATCH_ONLINE_PURGE_MAX_ROWS = 1000
|
||||
|
||||
@@ -42,7 +42,7 @@ JWT_REDIS_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
# -------- 数据库配置 --------
|
||||
# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
|
||||
# 数据库类型,固定使用'mysql'
|
||||
DB_TYPE = 'mysql'
|
||||
# 数据库主机
|
||||
DB_HOST = 'shierfeng-mysql'
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
# -------- 应用配置 --------
|
||||
# 应用运行环境
|
||||
APP_ENV = 'prod'
|
||||
# 应用名称
|
||||
APP_NAME = 'ShiErFeng-FastAPI'
|
||||
# 应用代理路径
|
||||
APP_ROOT_PATH = '/docker-api'
|
||||
# 应用主机
|
||||
APP_HOST = '0.0.0.0'
|
||||
# 应用端口
|
||||
APP_PORT = 9099
|
||||
# 应用版本
|
||||
APP_VERSION= '1.9.0'
|
||||
# 应用是否开启热重载
|
||||
APP_RELOAD = false
|
||||
# 应用工作进程数
|
||||
APP_WORKERS = 1
|
||||
# 应用是否开启IP归属区域查询
|
||||
APP_IP_LOCATION_QUERY = true
|
||||
# 应用是否为演示模式
|
||||
APP_DEMO_MODE = false
|
||||
# 应用是否允许账号同时登录
|
||||
APP_SAME_TIME_LOGIN = true
|
||||
# 应用是否禁用Swagger文档
|
||||
APP_DISABLE_SWAGGER = true
|
||||
# 应用是否禁用ReDoc文档
|
||||
APP_DISABLE_REDOC = true
|
||||
# 可信代理IP列表,多个值使用逗号分隔
|
||||
APP_TRUSTED_PROXY_IPS = '127.0.0.1,::1'
|
||||
# 可信代理跳数,单层Nginx代理通常为1
|
||||
APP_TRUSTED_PROXY_HOPS = 1
|
||||
|
||||
# -------- Jwt配置 --------
|
||||
# Jwt秘钥
|
||||
JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
|
||||
# Jwt算法
|
||||
JWT_ALGORITHM = 'HS256'
|
||||
# 令牌过期时间
|
||||
JWT_EXPIRE_MINUTES = 1440
|
||||
# redis中令牌过期时间
|
||||
JWT_REDIS_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
# -------- 数据库配置 --------
|
||||
# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
|
||||
DB_TYPE = 'postgresql'
|
||||
# 数据库主机
|
||||
DB_HOST = 'shierfeng-pg'
|
||||
# 数据库端口
|
||||
DB_PORT = 5432
|
||||
# 数据库用户名
|
||||
DB_USERNAME = 'postgres'
|
||||
# 数据库密码
|
||||
DB_PASSWORD = 'root'
|
||||
# 数据库名称
|
||||
DB_DATABASE = 'shierfeng-fastapi'
|
||||
# 是否开启sqlalchemy日志
|
||||
DB_ECHO = true
|
||||
# 允许溢出连接池大小的最大连接数
|
||||
DB_MAX_OVERFLOW = 10
|
||||
# 连接池大小,0表示连接数无限制
|
||||
DB_POOL_SIZE = 50
|
||||
# 连接回收时间(单位:秒)
|
||||
DB_POOL_RECYCLE = 3600
|
||||
# 连接池中没有线程可用时,最多等待的时间(单位:秒)
|
||||
DB_POOL_TIMEOUT = 30
|
||||
|
||||
# -------- Redis配置 --------
|
||||
# Redis主机
|
||||
REDIS_HOST = 'shierfeng-redis'
|
||||
# Redis端口
|
||||
REDIS_PORT = 6379
|
||||
# Redis用户名
|
||||
REDIS_USERNAME = ''
|
||||
# Redis密码
|
||||
REDIS_PASSWORD = ''
|
||||
# Redis数据库
|
||||
REDIS_DATABASE = 2
|
||||
|
||||
# -------- 日志配置 --------
|
||||
# 是否启用日志脱敏
|
||||
LOG_MASK_ENABLED = true
|
||||
# 日志脱敏占位符
|
||||
LOG_MASK_PLACEHOLDER = '******'
|
||||
# 以下三项为互补关系(非互斥):
|
||||
# 1) LOG_MASK_FIELDS:命中后执行全量脱敏
|
||||
# 2) LOG_PARTIAL_MASK_FIELDS:命中后执行部分脱敏
|
||||
# 3) LOG_CONFIG_SECRET_PATTERNS:按 configKey 关键词决定是否脱敏 configValue
|
||||
# 全量脱敏字段,多个值使用逗号分隔
|
||||
LOG_MASK_FIELDS = 'password,old_password,new_password,confirm_password,api_key,token,access_token,refresh_token,authorization,client_secret,secret,secret_key,private_key,private_key_pem,credential,credentials,sms_code,captcha_code,system_prompt'
|
||||
# 部分脱敏字段,多个值使用逗号分隔
|
||||
# 当前默认不对IP脱敏;如后期需要开启,可直接追加 ip,ipaddr,oper_ip,login_ip 并重启服务
|
||||
LOG_PARTIAL_MASK_FIELDS = 'phonenumber,phone,mobile,email'
|
||||
# 按配置键名识别敏感配置的关键词,多个值使用逗号分隔
|
||||
LOG_CONFIG_SECRET_PATTERNS = 'password,token,secret,key,private,credential,access,jwt,captcha,sms'
|
||||
# Redis Stream Key
|
||||
LOG_STREAM_KEY = 'log:stream'
|
||||
# Redis Stream 消费组名称
|
||||
LOG_STREAM_GROUP = 'log_aggregator'
|
||||
# Redis Stream 消费者名称前缀
|
||||
LOG_STREAM_CONSUMER_PREFIX = 'worker'
|
||||
# 每次读取的最大消息数量
|
||||
LOG_STREAM_BATCH_SIZE = 100
|
||||
# 阻塞读取等待时间(毫秒)
|
||||
LOG_STREAM_BLOCK_MS = 2000
|
||||
# Stream 最大长度(近似裁剪)
|
||||
LOG_STREAM_MAXLEN = 100000
|
||||
# Pending 回收最小空闲时间(毫秒)
|
||||
LOG_STREAM_CLAIM_IDLE_MS = 60000
|
||||
# Pending 回收检查间隔(毫秒)
|
||||
LOG_STREAM_CLAIM_INTERVAL_MS = 5000
|
||||
# 每次回收的最大消息数量
|
||||
LOG_STREAM_CLAIM_BATCH_SIZE = 100
|
||||
# 去重 Key 过期时间(秒)
|
||||
LOG_STREAM_DEDUP_TTL = 3600
|
||||
# 去重 Key 前缀
|
||||
LOG_STREAM_DEDUP_PREFIX = 'log:dedup'
|
||||
# stdout 输出是否为 JSON
|
||||
LOGURU_JSON = false
|
||||
# Loguru 最低输出级别
|
||||
LOGURU_LEVEL = 'INFO'
|
||||
# 是否输出到 stdout
|
||||
LOGURU_STDOUT = true
|
||||
# 是否启用文件日志
|
||||
LOG_FILE_ENABLED = true
|
||||
# 文件日志根目录
|
||||
LOG_FILE_BASE_DIR = 'logs'
|
||||
# 文件滚动策略
|
||||
LOGURU_ROTATION = '50MB'
|
||||
# 文件保留策略
|
||||
LOGURU_RETENTION = '30 days'
|
||||
# 文件压缩格式
|
||||
LOGURU_COMPRESSION = 'zip'
|
||||
# 实例标识(用于区分实例)
|
||||
LOG_INSTANCE_ID = 'dockerpg'
|
||||
# 服务名称(用于统一标识服务)
|
||||
LOG_SERVICE_NAME = 'shierfeng-fastapi-backend'
|
||||
# Worker 标识(auto 自动生成)
|
||||
LOG_WORKER_ID = 'auto'
|
||||
|
||||
# -------- 传输层加解密配置 --------
|
||||
# 是否启用传输层加解密
|
||||
TRANSPORT_CRYPTO_ENABLED = true
|
||||
# 传输层加解密模式:off=关闭,optional=明文/密文兼容,required=命中接口强制加密
|
||||
TRANSPORT_CRYPTO_MODE = 'optional'
|
||||
# 传输层加解密算法标识
|
||||
TRANSPORT_CRYPTO_ALGORITHM = 'RSA_OAEP_AES_256_GCM'
|
||||
# 当前启用的密钥版本标识
|
||||
TRANSPORT_CRYPTO_KID = 'default'
|
||||
# 传输层RSA位数,需与下方密钥对匹配
|
||||
TRANSPORT_CRYPTO_RSA_KEY_SIZE = 4096
|
||||
# 传输层公钥,默认提供一套可用示例值;Docker 部署前请替换为正式密钥
|
||||
TRANSPORT_CRYPTO_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwA2ooWpsxLzIVMJp7Wcv\nvR0Bu8paFn8NVPpzz+wGpUlwP5DGK6pBGItsglNMZx56MSYMp3zVyqB95XUVZ6Ub\nQVyHJ6BXXIMs/BpzcHCbyXR/wWG+pKxQ8UEwaHVhK8X21xW1R0kzzwhgMw51unEs\nA81kskVlDdGeNyaOJg5QuofEErCfR1y0e+iqd1PBpIwdEY5L+BzvbVtyhsPz6dBu\n9YbEbKs7JvNle5vjc72ebbMIeGejHFQRYxihuamPCAEylj1qqpHk8U+r+3icxQsj\n8F/TySLuOy37iVWfD+5ikLyou4ZDI3hOYnIYHl194ZM5xVyOGBD/xdqZadXTLqL6\nSrldwZfZDBl3EGgRby8yJqO6SqGgyvyGWXZAaHoPMmF+quu/nUooqnp0fpl+LCqX\nhK67kbYaA9xJeHRaP04cg16imB7sBIXBqzkyZtkhC2BtlL0h5X7dRlAddrQ23z6d\nWdJA3qe3iBSSM8pmedv+Jgfau/PFam2051HqFxvJEh/jnc6rq1aIjL/d4Kk5imIa\nQ6acv+v5N6QVFptlcx6I7j8yZJ7WUHZlB1IxVqfPb69+985eknZyLul60gyu0kPG\nYUecypUu2wiNDBAErZlUxEujMWgJDFYCSonHxtwr88gDInjP0lvwd/OgqjDQ7hSo\nRYxPhTbwo68RJDpImBjn+YsCAwEAAQ==\n-----END PUBLIC KEY-----'
|
||||
# 传输层私钥,必须与上方公钥成对使用;Docker 部署前请替换为正式密钥
|
||||
TRANSPORT_CRYPTO_PRIVATE_KEY = '-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCAgEAwA2ooWpsxLzIVMJp7WcvvR0Bu8paFn8NVPpzz+wGpUlwP5DG\nK6pBGItsglNMZx56MSYMp3zVyqB95XUVZ6UbQVyHJ6BXXIMs/BpzcHCbyXR/wWG+\npKxQ8UEwaHVhK8X21xW1R0kzzwhgMw51unEsA81kskVlDdGeNyaOJg5QuofEErCf\nR1y0e+iqd1PBpIwdEY5L+BzvbVtyhsPz6dBu9YbEbKs7JvNle5vjc72ebbMIeGej\nHFQRYxihuamPCAEylj1qqpHk8U+r+3icxQsj8F/TySLuOy37iVWfD+5ikLyou4ZD\nI3hOYnIYHl194ZM5xVyOGBD/xdqZadXTLqL6SrldwZfZDBl3EGgRby8yJqO6SqGg\nyvyGWXZAaHoPMmF+quu/nUooqnp0fpl+LCqXhK67kbYaA9xJeHRaP04cg16imB7s\nBIXBqzkyZtkhC2BtlL0h5X7dRlAddrQ23z6dWdJA3qe3iBSSM8pmedv+Jgfau/PF\nam2051HqFxvJEh/jnc6rq1aIjL/d4Kk5imIaQ6acv+v5N6QVFptlcx6I7j8yZJ7W\nUHZlB1IxVqfPb69+985eknZyLul60gyu0kPGYUecypUu2wiNDBAErZlUxEujMWgJ\nDFYCSonHxtwr88gDInjP0lvwd/OgqjDQ7hSoRYxPhTbwo68RJDpImBjn+YsCAwEA\nAQKCAgEAtH7hwjp8WPY3rPk+hqEYy7psO6q0ujnUNM5hc8MWh9caSJNuu/D51vCK\nHX3d63hITNK+x9ZaM2Jcj/9XC56vm+EzILngARFDgPf3EHC06UO1IfEUND3CoMqs\njS/sEDCwiXpccc/JgkUT7EgluwDL5hLuFWGE1NTxxlFU/U0s7/HTA+G9AFuPQHjt\nZNJxxozscOh0W44OM4/jdebJ4TBEaDqtdpgDMttDwEVFIizOrbi6ODbmpCQaZzDq\nJczGoTZG7c5w5jfRnD1NnDzB0apjr3DQYVOT5EiWA39Vy6o2NmMojX1ksfxKZtll\n83vYZ91vSR5waQvo5nFJB5+vJ0CCy4BZaV9/gQBP/OSElELfD0bGYidO7NURAJMW\njGc2B6uIog8BZ3ix02FO/30i5rWeDWM7McLol8+IP0Qcg473YvrCgmRZpprdc9HT\nHnB6BgIwu0ZBoP7CxW7pBm0ApwmOf7OvUdAu68kFQ4KxABuv+Qegu0vSsktBtmIs\ngUDzu/ekoayyOvFHVwVTd4QKOSa2gXb3TS7PP22j6QLL1OPy2fK2NBW0xrRDIgtw\nSx3Yo0dxCYkxJWz1nQYsdIjZbz2/xQPZZQ5yNeePwxaOKyDuA50elN86I9RR2gjL\nh9QMrvPG7DOlgmBG9+JsydQZf00G+x45Su/1fydA09X3AFtZLkkCggEBAOj8NY/x\n3G3/1qAkrcQYLwB0mjLNkgKchRIP8NMildk0nSub5SRUxleW2wp0kM0WqkFTXLMa\nFR0qCRjiyPOtBBZxLobIfGc6568LTKfg4N7VsCeAcTjFjSOE57iX5XzwhTy6ZCKe\nZFGFWfhj/a9lNQHneTcklDHq/oHjLJQ1FacbWNycIOX2Hk7SZKyJvyMutAem3BI+\nm5SLyj5648N2ylmz6f3eaTn8CyNCrUKWnPriQZfV0XF/Aalh4sq/I3fI6pqnYkHK\nuSoG9YHujGXKN2iOV/0krCiGj94l7JqvKGs4ZRoZdD+s1EugGD7qBK1wGATuEseg\nR6mS74ylgD88JPcCggEBANMGWrqhHOAtGeIewLbNob54+nqdwU/O+zk0RcTZErcD\naRZMTLPRlpdbgWbNgdfjFnbUx0WmIeTIboD2MaW5KOnDfV/x+eRhGtEmtNRvHA/o\nHK6uYzok7Ekm7oc298CYrfRbEWVx3m4+mHmU+rsvBeof664zpEEIsSwm+NaAnjz8\nru0U8xw8KWLB8F/Q4J4p4D0yVKCmAXmrrTgEYm1H355JlAlmiu6t6YPS2Xd+gNFD\ntWBsQXYnxEZyPhoD1Q4SQjQmQMEN7+lIHm8f/nMwwUkAd3y6D1w/Uu81Gqa/9eAh\nz3woK++tj/U1CMMTWXJumIyJdH28CWQJa9VdJBoFbw0CggEAdIBhPDhh1DNhHDUb\nGvpIzn5/+LVotJuVwwFrl/gsXC9+BCdxPmiRwYyyvRiqq5MQ0ZegvAJn2myBngsR\nFyBF8f7omAc6hdgjsBkDXNMLPwI1kpscgpnuSHuV720tGPugdExz9Ael/EmlQpql\naQY/qlMX/uXwoMF0QHjbesTMrgHzvmTn4nXek+WK5+f9Rtd8uHLMiub5nx8Do5iJ\nudz1tENN119W2OLaougdgTWVC7MFv6nwkENFDnDfGijX0HcMMQdQD7wSORy/uRgS\n5ndmm4bKItAqsh0PZbMrC/JYUL0jeRiPU6PViHdmiFc2vY9Ww2hUxbO1Aetyk67S\nYUxu4wKCAQAvSOQ5n3JV9SUwms156LfOc1NE+GZhmLKITeM4SZ+87IG2omAphtQk\nlDd5rqDinBrjg6gnPiOoHRVNxly3krbNMqW8Qv9Iok2dAfxRAZibI9qRdbf8Rlu/\nxH58Q9/eAxgvgdxzC8HYmCQYUj6ghNfhb0ejICU1AVqZ1x25CtbqOYCE1UXoVL69\nR1GyVp6OMjnx3H5EBQ6dkc9dlvXrIMjFshz+wkBtXQDPLgbWlL0OpwpUUc2VSTHc\nyyqJL8Skw4icINovqAzTC/rt0ZB1hT46OmWLyDE67WGLAi43oRnaIBla67F0okJ4\nomqVM5e+YXPDQeWdau37wXStOZKmVgNdAoIBADojGo0h9mBgnr+u1oYB+mKx6LVD\n3TTy45IB1ikqprBnSjgXNbQycsTbl+qDo5ge+KqlPNk6Scvn0L7k43/VB9Y5qK2s\nBgxa1KgdeC5WUNU0rs/1UKIODA5SlWIk6JqekiAE+glljVZ5E53l93gPL3uRcvr5\nSD5CPe+qqoBh9nYIRJDogP0e2xV11EuE10j1WxuLkHL5//hePGPpgnb0/rZkP+vb\nkzwz1fTv88kQDnRk6uIe0L78iFTpEwAnlDmuMJ4KafKnujaR5VVCoI7bwryCp4+f\n8zyU7ZpKo/2EE8bYmxHMDPiYByJxCwAbm2Xn7Kw4H/17MLJOxg8685A8jyA=\n-----END RSA PRIVATE KEY-----'
|
||||
# 历史密钥对配置,JSON数组格式,密钥轮换说明见 docs/transport_crypto_deployment.md
|
||||
TRANSPORT_CRYPTO_LEGACY_KEY_PAIRS = '[]'
|
||||
# 公钥缓存有效期(秒)
|
||||
TRANSPORT_CRYPTO_PUBLIC_KEY_TTL_SECONDS = 3600
|
||||
# 前端传输加密策略缓存有效期(秒)
|
||||
TRANSPORT_CRYPTO_FRONTEND_CONFIG_TTL_SECONDS = 300
|
||||
# GET/DELETE 请求参数加密后的最大 URL 长度
|
||||
TRANSPORT_CRYPTO_MAX_GET_URL_LENGTH = 4096
|
||||
# 请求时间窗允许偏差(秒)
|
||||
TRANSPORT_CRYPTO_CLOCK_SKEW_SECONDS = 120
|
||||
# 防重放随机数有效期(秒)
|
||||
TRANSPORT_CRYPTO_REPLAY_TTL_SECONDS = 300
|
||||
# 启用传输层加密的路径列表,多个值使用逗号分隔,留空表示默认全部启用
|
||||
TRANSPORT_CRYPTO_ENABLED_PATHS = ''
|
||||
# 强制要求传输层加密的路径列表,多个值使用逗号分隔
|
||||
TRANSPORT_CRYPTO_REQUIRED_PATHS = ''
|
||||
# 排除传输层加密的路径列表,多个值使用逗号分隔
|
||||
TRANSPORT_CRYPTO_EXCLUDE_PATHS = '/openapi.json,/docs,/docs/oauth2-redirect,/redoc,/transport/crypto/frontend-config,/transport/crypto/public-key,/common/download,/common/download/resource'
|
||||
@@ -42,7 +42,7 @@ JWT_REDIS_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
# -------- 数据库配置 --------
|
||||
# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
|
||||
# 数据库类型,固定使用'mysql'
|
||||
DB_TYPE = 'mysql'
|
||||
# 数据库主机
|
||||
DB_HOST = '127.0.0.1'
|
||||
@@ -171,3 +171,5 @@ TRANSPORT_CRYPTO_ENABLED_PATHS = ''
|
||||
TRANSPORT_CRYPTO_REQUIRED_PATHS = ''
|
||||
# 排除传输层加密的路径列表,多个值使用逗号分隔
|
||||
TRANSPORT_CRYPTO_EXCLUDE_PATHS = '/openapi.json,/docs,/docs/oauth2-redirect,/redoc,/transport/crypto/frontend-config,/transport/crypto/public-key,/common/download,/common/download/resource'
|
||||
# 测试批次在线清理最大数据行数;0 表示禁止在线清理
|
||||
TEST_BATCH_ONLINE_PURGE_MAX_ROWS = 1000
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
FROM python:3.10
|
||||
WORKDIR /app
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 安装依赖
|
||||
RUN pip install --no-cache-dir -r requirements-pg.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 9099
|
||||
|
||||
# 启动命令
|
||||
CMD ["shierfeng", "app", "run", "--env=dockerpg"]
|
||||
@@ -1,7 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
DEFAULT_ENVIRONMENTS = ('dev', 'prod', 'dockermy', 'dockerpg')
|
||||
DEFAULT_ENVIRONMENTS = ('dev', 'prod', 'dockermy')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -28,7 +28,7 @@ GENERAL_COPY = {
|
||||
'action_confirm_hint': '按键提示 · [Enter] 确认执行 [Esc] 取消返回',
|
||||
'more_detail_hint': '> 进入对应页面查看更多',
|
||||
'tui_command_help': '进入只读巡检工作台',
|
||||
'tui_missing_dependency_hint': '请重新执行 `pip install -r requirements.txt` 或 `pip install -r requirements-pg.txt`',
|
||||
'tui_missing_dependency_hint': '请重新执行 `pip install -r requirements.txt`',
|
||||
'tui_missing_dependency_message': '当前环境未安装 TUI 可选依赖,无法启动 `shierfeng tui`',
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
DEFAULT_ENVIRONMENTS = ('dev', 'prod', 'dockermy', 'dockerpg')
|
||||
DEFAULT_ENVIRONMENTS = ('dev', 'prod', 'dockermy')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -28,7 +28,7 @@ GENERAL_COPY = {
|
||||
'action_confirm_hint': '按键提示 · [Enter] 确认执行 [Esc] 取消返回',
|
||||
'more_detail_hint': '> 进入对应页面查看更多',
|
||||
'tui_command_help': '进入只读巡检工作台',
|
||||
'tui_missing_dependency_hint': '请重新执行 `pip install -r requirements.txt` 或 `pip install -r requirements-pg.txt`',
|
||||
'tui_missing_dependency_hint': '请重新执行 `pip install -r requirements.txt`',
|
||||
'tui_missing_dependency_message': '当前环境未安装 TUI 可选依赖,无法启动 `shierfeng tui`',
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ class Log:
|
||||
log_type: Literal['login', 'operation'] | None = 'operation',
|
||||
request_log_mode: Literal['full', 'none', 'summary', 'include', 'exclude'] = 'full',
|
||||
response_log_mode: Literal['full', 'none', 'summary', 'include', 'exclude'] = 'full',
|
||||
oper_url_mode: Literal['full', 'route'] = 'full',
|
||||
request_include_fields: tuple[str, ...] | None = None,
|
||||
response_include_fields: tuple[str, ...] | None = None,
|
||||
request_exclude_fields: tuple[str, ...] | None = None,
|
||||
@@ -130,6 +131,7 @@ class Log:
|
||||
:param log_type: 日志类型;`login`表示登录日志,仅落登录信息,`operation`表示操作日志,会落请求/响应摘要与操作人信息
|
||||
:param request_log_mode: 请求日志记录模式;`full`记录脱敏后的完整请求,`none`不记录请求体,`summary`记录请求摘要,`include`仅记录request_include_fields指定的字段,`exclude`记录完整请求后排除request_exclude_fields指定的字段
|
||||
:param response_log_mode: 响应日志记录模式;`full`记录脱敏后的完整响应,`none`不记录响应体,`summary`记录响应摘要,`include`仅记录response_include_fields指定的字段,`exclude`记录完整响应后排除response_exclude_fields指定的字段
|
||||
:param oper_url_mode: 操作URL记录模式;`full`记录实际请求路径,`route`记录路由模板,适合路径中包含敏感标识的接口
|
||||
:param request_include_fields: 请求日志白名单字段路径,仅在request_log_mode='include'时生效;推荐从path_params/query_params/json_body/form_data/files/raw_body开始,字段名支持 snake_case 与 camelCase 自动兼容
|
||||
:param response_include_fields: 响应日志白名单字段路径,仅在response_log_mode='include'时生效;推荐从code/msg/data/rows/success/time开始,字段名支持 snake_case 与 camelCase 自动兼容
|
||||
:param request_exclude_fields: 请求日志排除字段路径,仅在request_log_mode='exclude'时生效;推荐从path_params/query_params/json_body/form_data/files/raw_body开始,字段名支持 snake_case 与 camelCase 自动兼容
|
||||
@@ -141,6 +143,7 @@ class Log:
|
||||
self.log_type = log_type
|
||||
self.request_log_mode = request_log_mode
|
||||
self.response_log_mode = response_log_mode
|
||||
self.oper_url_mode = oper_url_mode
|
||||
self.request_include_fields = request_include_fields or ()
|
||||
self.response_include_fields = response_include_fields or ()
|
||||
self.request_exclude_fields = request_exclude_fields or ()
|
||||
@@ -166,7 +169,7 @@ class Log:
|
||||
# 获取操作类型
|
||||
operator_type = self._get_oper_type(user_agent)
|
||||
# 获取请求的url
|
||||
oper_url = request.url.path
|
||||
oper_url = self._get_oper_url(request)
|
||||
# 获取请求ip
|
||||
oper_ip = ClientIPUtil.get_client_ip(request)
|
||||
# 获取请求ip归属区域
|
||||
@@ -281,6 +284,21 @@ class Log:
|
||||
|
||||
return func_path
|
||||
|
||||
def _get_oper_url(self, request: Request) -> str:
|
||||
"""
|
||||
获取操作日志中的请求URL。
|
||||
|
||||
路由模板模式只保留路由结构,不把路径参数(例如二维码固定标识)写入日志。
|
||||
|
||||
:param request: 当前请求
|
||||
:return: 用于操作日志的URL
|
||||
"""
|
||||
if self.oper_url_mode == 'route':
|
||||
route_path = getattr(request.scope.get('route'), 'path', None)
|
||||
if route_path:
|
||||
return route_path
|
||||
return request.url.path
|
||||
|
||||
@staticmethod
|
||||
def _limit_log_text(log_text: str, max_length: int, overflow_text: str) -> str:
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from config.env import DataBaseConfig
|
||||
|
||||
|
||||
class CommonConstant:
|
||||
@@ -549,32 +548,10 @@ class GenConstant:
|
||||
TREE_NAME = 'treeName'
|
||||
PARENT_MENU_ID = 'parentMenuId'
|
||||
PARENT_MENU_NAME = 'parentMenuName'
|
||||
COLUMNTYPE_STR = (
|
||||
['character varying', 'varchar', 'character', 'char']
|
||||
if DataBaseConfig.db_type == 'postgresql'
|
||||
else ['char', 'varchar', 'nvarchar', 'varchar2']
|
||||
)
|
||||
COLUMNTYPE_TEXT = (
|
||||
['text', 'citext'] if DataBaseConfig.db_type == 'postgresql' else ['tinytext', 'text', 'mediumtext', 'longtext']
|
||||
)
|
||||
COLUMNTYPE_TIME = (
|
||||
[
|
||||
'date',
|
||||
'time',
|
||||
'time with time zone',
|
||||
'time without time zone',
|
||||
'timestamp',
|
||||
'timestamp with time zone',
|
||||
'timestamp without time zone',
|
||||
'interval',
|
||||
]
|
||||
if DataBaseConfig.db_type == 'postgresql'
|
||||
else ['datetime', 'time', 'date', 'timestamp']
|
||||
)
|
||||
COLUMNTYPE_GEOMETRY = (
|
||||
['point', 'line', 'lseg', 'box', 'path', 'polygon', 'circle']
|
||||
if DataBaseConfig.db_type == 'postgresql'
|
||||
else [
|
||||
COLUMNTYPE_STR = ['char', 'varchar', 'nvarchar', 'varchar2']
|
||||
COLUMNTYPE_TEXT = ['tinytext', 'text', 'mediumtext', 'longtext']
|
||||
COLUMNTYPE_TIME = ['datetime', 'time', 'date', 'timestamp']
|
||||
COLUMNTYPE_GEOMETRY = [
|
||||
'geometry',
|
||||
'point',
|
||||
'linestring',
|
||||
@@ -584,7 +561,6 @@ class GenConstant:
|
||||
'multipolygon',
|
||||
'geometrycollection',
|
||||
]
|
||||
)
|
||||
COLUMNTYPE_NUMBER = [
|
||||
'tinyint',
|
||||
'smallint',
|
||||
@@ -619,64 +595,7 @@ class GenConstant:
|
||||
QUERY_LIKE = 'LIKE'
|
||||
QUERY_EQ = 'EQ'
|
||||
REQUIRE = '1'
|
||||
DB_TO_SQLALCHEMY_TYPE_MAPPING = (
|
||||
{
|
||||
'boolean': 'Boolean',
|
||||
'smallint': 'SmallInteger',
|
||||
'integer': 'Integer',
|
||||
'bigint': 'BigInteger',
|
||||
'real': 'Float',
|
||||
'double precision': 'Float',
|
||||
'numeric': 'Numeric',
|
||||
'character varying': 'String',
|
||||
'character': 'String',
|
||||
'text': 'Text',
|
||||
'bytea': 'LargeBinary',
|
||||
'date': 'Date',
|
||||
'time': 'Time',
|
||||
'time with time zone': 'Time',
|
||||
'time without time zone': 'Time',
|
||||
'timestamp': 'DateTime',
|
||||
'timestamp with time zone': 'DateTime',
|
||||
'timestamp without time zone': 'DateTime',
|
||||
'interval': 'Interval',
|
||||
'json': 'JSON',
|
||||
'jsonb': 'JSONB',
|
||||
'uuid': 'Uuid',
|
||||
'inet': 'INET',
|
||||
'cidr': 'CIDR',
|
||||
'macaddr': 'MACADDR',
|
||||
'point': 'Geometry',
|
||||
'line': 'Geometry',
|
||||
'lseg': 'Geometry',
|
||||
'box': 'Geometry',
|
||||
'path': 'Geometry',
|
||||
'polygon': 'Geometry',
|
||||
'circle': 'Geometry',
|
||||
'bit': 'Bit',
|
||||
'bit varying': 'Bit',
|
||||
'tsvector': 'TSVECTOR',
|
||||
'tsquery': 'TSQUERY',
|
||||
'xml': 'String',
|
||||
'array': 'ARRAY',
|
||||
'composite': 'JSON',
|
||||
'enum': 'Enum',
|
||||
'range': 'Range',
|
||||
'money': 'Numeric',
|
||||
'pg_lsn': 'BigInteger',
|
||||
'txid_snapshot': 'String',
|
||||
'oid': 'BigInteger',
|
||||
'regproc': 'String',
|
||||
'regclass': 'String',
|
||||
'regtype': 'String',
|
||||
'regrole': 'String',
|
||||
'regnamespace': 'String',
|
||||
'int2vector': 'ARRAY',
|
||||
'oidvector': 'ARRAY',
|
||||
'pg_node_tree': 'Text',
|
||||
}
|
||||
if DataBaseConfig.db_type == 'postgresql'
|
||||
else {
|
||||
DB_TO_SQLALCHEMY_TYPE_MAPPING = {
|
||||
# 数值类型
|
||||
'TINYINT': 'SmallInteger',
|
||||
'SMALLINT': 'SmallInteger',
|
||||
@@ -722,65 +641,7 @@ class GenConstant:
|
||||
'MULTIPOLYGON': 'Geometry',
|
||||
'GEOMETRYCOLLECTION': 'Geometry',
|
||||
}
|
||||
)
|
||||
DB_TO_PYTHON_TYPE_MAPPING = (
|
||||
{
|
||||
'boolean': 'bool',
|
||||
'smallint': 'int',
|
||||
'integer': 'int',
|
||||
'bigint': 'int',
|
||||
'real': 'float',
|
||||
'double precision': 'float',
|
||||
'numeric': 'Decimal',
|
||||
'character varying': 'str',
|
||||
'character': 'str',
|
||||
'text': 'str',
|
||||
'bytea': 'bytes',
|
||||
'date': 'date',
|
||||
'time': 'time',
|
||||
'time with time zone': 'time',
|
||||
'time without time zone': 'time',
|
||||
'timestamp': 'datetime',
|
||||
'timestamp with time zone': 'datetime',
|
||||
'timestamp without time zone': 'datetime',
|
||||
'interval': 'timedelta',
|
||||
'json': 'dict',
|
||||
'jsonb': 'dict',
|
||||
'uuid': 'str',
|
||||
'inet': 'str',
|
||||
'cidr': 'str',
|
||||
'macaddr': 'str',
|
||||
'point': 'list',
|
||||
'line': 'list',
|
||||
'lseg': 'list',
|
||||
'box': 'list',
|
||||
'path': 'list',
|
||||
'polygon': 'list',
|
||||
'circle': 'list',
|
||||
'bit': 'int',
|
||||
'bit varying': 'int',
|
||||
'tsvector': 'str',
|
||||
'tsquery': 'str',
|
||||
'xml': 'str',
|
||||
'array': 'list',
|
||||
'composite': 'dict',
|
||||
'enum': 'str',
|
||||
'range': 'list',
|
||||
'money': 'Decimal',
|
||||
'pg_lsn': 'int',
|
||||
'txid_snapshot': 'str',
|
||||
'oid': 'int',
|
||||
'regproc': 'str',
|
||||
'regclass': 'str',
|
||||
'regtype': 'str',
|
||||
'regrole': 'str',
|
||||
'regnamespace': 'str',
|
||||
'int2vector': 'list',
|
||||
'oidvector': 'list',
|
||||
'pg_node_tree': 'str',
|
||||
}
|
||||
if DataBaseConfig.db_type == 'postgresql'
|
||||
else {
|
||||
DB_TO_PYTHON_TYPE_MAPPING = {
|
||||
# 数值类型
|
||||
'TINYINT': 'int',
|
||||
'SMALLINT': 'int',
|
||||
@@ -826,4 +687,3 @@ class GenConstant:
|
||||
'MULTIPOLYGON': 'bytes',
|
||||
'GEOMETRYCOLLECTION': 'bytes',
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Literal
|
||||
|
||||
from exceptions.exception import LoginException
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_business.domain.test_batch_scope import TestBatchScope
|
||||
|
||||
# 定义上下文变量
|
||||
# 存储当前请求的编译后的排除路由模式列表
|
||||
@@ -12,6 +13,8 @@ current_exclude_patterns: ContextVar[
|
||||
] = ContextVar('current_exclude_patterns', default=None)
|
||||
# 存储当前用户信息
|
||||
current_user: ContextVar[CurrentUserModel | None] = ContextVar('current_user', default=None)
|
||||
# 存储当前请求已校验的测试批次作用域
|
||||
current_test_batch_scope: ContextVar[TestBatchScope | None] = ContextVar('current_test_batch_scope', default=None)
|
||||
|
||||
|
||||
class RequestContext:
|
||||
@@ -69,6 +72,21 @@ class RequestContext:
|
||||
raise LoginException(data='', message='当前用户信息为空,请检查是否已登录')
|
||||
return _current_user
|
||||
|
||||
@staticmethod
|
||||
def set_current_test_batch_scope(scope: TestBatchScope) -> Token:
|
||||
"""
|
||||
设置当前请求已校验的测试批次作用域
|
||||
|
||||
:param scope: 测试批次作用域
|
||||
:return: 上下文变量令牌,用于重置
|
||||
"""
|
||||
return current_test_batch_scope.set(scope)
|
||||
|
||||
@staticmethod
|
||||
def get_current_test_batch_scope() -> TestBatchScope | None:
|
||||
"""获取当前请求已校验的测试批次作用域。"""
|
||||
return current_test_batch_scope.get()
|
||||
|
||||
@staticmethod
|
||||
def reset_current_exclude_patterns(token: Token) -> None:
|
||||
"""
|
||||
@@ -87,6 +105,11 @@ class RequestContext:
|
||||
"""
|
||||
current_user.reset(token)
|
||||
|
||||
@staticmethod
|
||||
def reset_current_test_batch_scope(token: Token) -> None:
|
||||
"""重置当前请求的测试批次作用域。"""
|
||||
current_test_batch_scope.reset(token)
|
||||
|
||||
@staticmethod
|
||||
def clear_all() -> None:
|
||||
"""
|
||||
@@ -94,3 +117,4 @@ class RequestContext:
|
||||
"""
|
||||
current_exclude_patterns.set(None)
|
||||
current_user.set(None)
|
||||
current_test_batch_scope.set(None)
|
||||
|
||||
@@ -13,11 +13,6 @@ def build_async_sqlalchemy_database_url() -> str:
|
||||
|
||||
:return: 异步 SQLAlchemy 数据库连接 URL
|
||||
"""
|
||||
if DataBaseConfig.db_type == 'postgresql':
|
||||
return (
|
||||
f'postgresql+asyncpg://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
|
||||
f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
|
||||
)
|
||||
return (
|
||||
f'mysql+asyncmy://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
|
||||
f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
|
||||
@@ -33,11 +28,6 @@ def build_sync_sqlalchemy_database_url() -> str:
|
||||
|
||||
:return: 同步 SQLAlchemy 数据库连接 URL
|
||||
"""
|
||||
if DataBaseConfig.db_type == 'postgresql':
|
||||
return (
|
||||
f'postgresql+psycopg2://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
|
||||
f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
|
||||
)
|
||||
return (
|
||||
f'mysql+pymysql://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
|
||||
f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
|
||||
|
||||
@@ -47,7 +47,7 @@ class DataBaseSettings(BaseSettings):
|
||||
数据库配置
|
||||
"""
|
||||
|
||||
db_type: Literal['mysql', 'postgresql'] = 'mysql'
|
||||
db_type: Literal['mysql'] = 'mysql'
|
||||
db_host: str = '127.0.0.1'
|
||||
db_port: int = 3306
|
||||
db_username: str = 'root'
|
||||
@@ -62,8 +62,6 @@ class DataBaseSettings(BaseSettings):
|
||||
@computed_field
|
||||
@property
|
||||
def sqlglot_parse_dialect(self) -> str:
|
||||
if self.db_type == 'postgresql':
|
||||
return 'postgres'
|
||||
return self.db_type
|
||||
|
||||
|
||||
@@ -144,6 +142,15 @@ class TransportCryptoSettings(BaseSettings):
|
||||
)
|
||||
|
||||
|
||||
class TestBatchSettings(BaseSettings):
|
||||
"""
|
||||
测试批次安全配置
|
||||
"""
|
||||
|
||||
test_batch_preview_ttl_seconds: int = 600
|
||||
test_batch_online_purge_max_rows: int = 0
|
||||
|
||||
|
||||
class GenSettings:
|
||||
"""
|
||||
代码生成配置
|
||||
@@ -265,6 +272,12 @@ class GetConfig:
|
||||
"""
|
||||
return TransportCryptoSettings()
|
||||
|
||||
def get_test_batch_config(self) -> TestBatchSettings:
|
||||
"""
|
||||
获取测试批次安全配置
|
||||
"""
|
||||
return TestBatchSettings()
|
||||
|
||||
def get_gen_config(self) -> GenSettings:
|
||||
"""
|
||||
获取代码生成配置
|
||||
@@ -328,6 +341,8 @@ RedisConfig = get_config.get_redis_config()
|
||||
LogConfig = get_config.get_log_config()
|
||||
# 传输层加解密配置
|
||||
TransportCryptoConfig = get_config.get_transport_crypto_config()
|
||||
# 测试批次安全配置
|
||||
TestBatchConfig = get_config.get_test_batch_config()
|
||||
# 代码生成配置
|
||||
GenConfig = get_config.get_gen_config()
|
||||
# 上传配置
|
||||
|
||||
@@ -38,23 +38,16 @@ shierfeng --help
|
||||
|
||||
### 2.2 安装依赖
|
||||
|
||||
MySQL 版本:
|
||||
安装依赖(统一使用 MySQL):
|
||||
|
||||
```bash
|
||||
cd shierfeng-fastapi-backend
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
PostgreSQL 版本:
|
||||
|
||||
```bash
|
||||
cd shierfeng-fastapi-backend
|
||||
pip3 install -r requirements-pg.txt
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `requirements*.txt` 已包含当前项目自身安装项 `.`,因此不需要额外执行 `pip install -e .`
|
||||
- `requirements.txt` 已包含当前项目自身安装项 `.`,因此不需要额外执行 `pip install -e .`
|
||||
- 安装完成后,`shierfeng` 会随当前 Python 环境一起可用
|
||||
|
||||
如果本地使用 Conda,推荐先进入项目环境再执行命令:
|
||||
@@ -65,7 +58,7 @@ cd shierfeng-fastapi-backend
|
||||
shierfeng --help
|
||||
```
|
||||
|
||||
`textual` 已包含在现有依赖文件中,因此安装 `requirements.txt` 或 `requirements-pg.txt` 后即可直接使用 TUI。
|
||||
`textual` 已包含在现有依赖文件中,安装 `requirements.txt` 后即可直接使用 TUI。
|
||||
|
||||
### 2.3 第一个命令
|
||||
|
||||
@@ -104,7 +97,6 @@ CLI 不单独维护配置系统,仍然复用项目原有的 `config/env.py`
|
||||
- `--env=dev` -> `.env.dev`
|
||||
- `--env=prod` -> `.env.prod`
|
||||
- `--env=dockermy` -> `.env.dockermy`
|
||||
- `--env=dockerpg` -> `.env.dockerpg`
|
||||
|
||||
### 3.3 帮助命令
|
||||
|
||||
@@ -494,7 +486,7 @@ shierfeng tui --env=prod
|
||||
- 当前 TUI 是只读巡检工作台,页面内的写操作入口会通过确认弹窗或向导二次确认
|
||||
- 页面切换快捷键为 `D/A/O/B/C/T/G/P/E`,分别对应总览、应用、运维、数据库、缓存、任务、代码生成、参数配置、加密
|
||||
- 通用快捷键包括 `R` 刷新、`Q` 退出、`S` 聚焦侧栏、`←/→` 切换焦点或区域、`J/K` 滚动、`PgUp/PgDn` 翻页、`Home/End` 首尾跳转
|
||||
- 若当前 Python 环境缺少 TUI 依赖,`shierfeng tui` 会返回失败结果并提示重新执行 `pip install -r requirements.txt` 或 `pip install -r requirements-pg.txt`
|
||||
- 若当前 Python 环境缺少 TUI 依赖,`shierfeng tui` 会返回失败结果并提示重新执行 `pip install -r requirements.txt`
|
||||
|
||||
## 6. 危险命令清单
|
||||
|
||||
@@ -814,7 +806,7 @@ shierfeng ops health --env=dev --output=json
|
||||
请依次确认:
|
||||
|
||||
- 当前目录是否为 `shierfeng-fastapi-backend`
|
||||
- 当前 Python 环境是否执行过 `pip install -r requirements.txt` 或 `pip install -r requirements-pg.txt`
|
||||
- 当前 Python 环境是否执行过 `pip install -r requirements.txt`
|
||||
- 当前终端是否真的使用了安装依赖的那个 Python/Conda 环境
|
||||
|
||||
### 8.2 命令报数据库或 Redis 连接失败
|
||||
|
||||
@@ -64,23 +64,20 @@ TRANSPORT_CRYPTO_MAX_GET_URL_LENGTH=4096
|
||||
当前项目的 Docker 部署使用:
|
||||
|
||||
- `shierfeng-fastapi-test/docker-compose.test.my.yml` + `shierfeng-fastapi-backend/Dockerfile.my`
|
||||
- `shierfeng-fastapi-test/docker-compose.test.pg.yml` + `shierfeng-fastapi-backend/Dockerfile.pg`
|
||||
|
||||
后端容器启动命令分别是:
|
||||
|
||||
- `shierfeng app run --env=dockermy`
|
||||
- `shierfeng app run --env=dockerpg`
|
||||
|
||||
所以 Docker 环境需要直接在以下文件中配置传输层密钥:
|
||||
|
||||
- `shierfeng-fastapi-backend/.env.dockermy`
|
||||
- `shierfeng-fastapi-backend/.env.dockerpg`
|
||||
|
||||
配置方式与生产环境相同;`.env.dockermy` / `.env.dockerpg` 里也已经默认提供一套可用示例值,正式部署前请替换为正式密钥。
|
||||
配置方式与生产环境相同;`.env.dockermy` 里已经默认提供一套可用示例值,正式部署前请替换为正式密钥。
|
||||
|
||||
使用时只需要:
|
||||
|
||||
1. 修改对应的 `.env.dockermy` 或 `.env.dockerpg`
|
||||
1. 修改 `.env.dockermy`
|
||||
2. 重新构建并启动 Docker 服务
|
||||
|
||||
## 密钥生成
|
||||
|
||||
@@ -8,3 +8,40 @@
|
||||
2026-09-20 10:04:32.035 | 1d12b62bcc9b41c191fe086905a49beb | b1fdb9fba55447d8976b3ed8c2bc0280 | 2f4a44c679a34d8781a45191abfa7987 | 16993-cd71f0 | ERROR | common.annotation.log_annotation:wrapper:199 - 新增用户admin1失败,邮箱账号已存在
|
||||
2026-09-20 10:04:43.176 | e3982edb2a9a474aba72beef8bf68e9f | ab43381480ee4fda9808645d08c25635 | 537175b887df487eb1b7e6739de1c9da | 16993-cd71f0 | ERROR | common.annotation.log_annotation:wrapper:199 - 新增用户shierfeng失败,邮箱账号已存在
|
||||
2026-09-20 10:05:45.737 | a2482c9df9f649259d08e3642bdf0488 | e66b674fdac647e4b0ef9c54851001d9 | 5de9f2b06af74d5396289bb3ea8ec2b8 | 16993-cd71f0 | ERROR | common.annotation.log_annotation:wrapper:199 - 新密码不能与旧密码相同
|
||||
2026-09-20 11:42:14.773 | | | | 31890-bb233f | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-784' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.136 seconds
|
||||
2026-09-20 11:42:16.249 | | | | 31890-bb233f | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-964' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.105 seconds
|
||||
2026-09-20 12:17:22.659 | | | | 57933-537a04 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-13' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.113 seconds
|
||||
2026-09-20 12:17:29.067 | | | | 58127-ed7958 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-964' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.105 seconds
|
||||
2026-09-20 12:46:48.547 | 5d5c50d3b58b455aa80a68c91a1a5b62 | ae9e09d7e564480ebeb91e41fdcd4b6d | 77fe7828932949f894e7c4cd8887646b | 16993-cd71f0 | WARNING | module_admin.service.login_service:__check_login_captcha:169 - 验证码已失效
|
||||
2026-09-20 12:46:48.549 | 5d5c50d3b58b455aa80a68c91a1a5b62 | ae9e09d7e564480ebeb91e41fdcd4b6d | 77fe7828932949f894e7c4cd8887646b | 16993-cd71f0 | WARNING | common.annotation.log_annotation:wrapper:194 - 验证码已失效
|
||||
2026-09-20 13:18:52.359 | dd3c9b48716a4a58b31b4625f20634aa | 13c18f6b346d4cdf99632eb435cf2d87 | 21419c5c356d438a8e159b3010860caa | 16993-cd71f0 | WARNING | module_admin.service.login_service:get_current_user:279 - 用户token已失效,请重新登录
|
||||
2026-09-20 17:47:23.214 | | | | 58041-740809 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-181' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.177 seconds
|
||||
2026-09-20 17:47:23.620 | | | | 58041-740809 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-247' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.109 seconds
|
||||
2026-09-20 17:47:23.869 | | | | 58041-740809 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-283' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.121 seconds
|
||||
2026-09-20 17:49:35.457 | | | | 60128-363e41 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-784' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.110 seconds
|
||||
2026-09-20 17:50:37.968 | | | | 61359-b363d2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-784' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.113 seconds
|
||||
2026-09-20 17:53:57.014 | | | | 64317-1bf7cd | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-76' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.129 seconds
|
||||
2026-09-20 17:53:57.604 | | | | 64317-1bf7cd | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-181' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.175 seconds
|
||||
2026-09-20 17:54:02.489 | | | | 64317-1bf7cd | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-964' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.124 seconds
|
||||
2026-09-20 18:02:59.450 | | | | 72520-28953b | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-181' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.164 seconds
|
||||
2026-09-20 18:03:00.429 | | | | 72520-28953b | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-289' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.137 seconds
|
||||
2026-09-20 18:03:05.635 | | | | 72520-28953b | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-784' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.283 seconds
|
||||
2026-09-20 18:03:07.372 | | | | 72520-28953b | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-964' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.139 seconds
|
||||
2026-09-20 18:09:24.200 | | | | 80215-4e32b1 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-253' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.158 seconds
|
||||
2026-09-20 18:09:28.242 | | | | 80215-4e32b1 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-772' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.105 seconds
|
||||
2026-09-20 18:09:29.458 | | | | 80215-4e32b1 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-994' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.106 seconds
|
||||
2026-09-20 18:14:10.968 | | | | 84308-7ecd65 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-676' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.120 seconds
|
||||
2026-09-20 18:14:13.598 | | | | 84308-7ecd65 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-979' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.114 seconds
|
||||
2026-09-20 18:20:04.515 | | | | 87818-33a210 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-979' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.109 seconds
|
||||
2026-09-20 18:29:21.351 | | | | 92649-eff25d | WARNING | module_admin.service.login_service:authenticate_user:110 - 打印User:$2b$12$sJ2mSJgH2GsXKAfijj1/yu.6Pks28OyfkpLSii0DKf5nsXfi6O2t6
|
||||
2026-09-20 18:29:21.590 | | | | 92649-eff25d | WARNING | module_admin.service.login_service:authenticate_user:111 - 打印User:False
|
||||
2026-09-20 18:29:21.591 | | | | 92649-eff25d | WARNING | module_admin.service.login_service:authenticate_user:135 - 密码错误
|
||||
2026-09-20 18:29:48.210 | | | | 93061-767fb0 | WARNING | module_admin.service.login_service:authenticate_user:110 - 打印User:$2b$12$gXCowPRYJUknyms/qz3KEOjHNaFdlszai/ptNLG8kca3C1daZ9xey
|
||||
2026-09-20 18:29:48.429 | | | | 93061-767fb0 | WARNING | module_admin.service.login_service:authenticate_user:111 - 打印User:False
|
||||
2026-09-20 18:29:48.430 | | | | 93061-767fb0 | WARNING | module_admin.service.login_service:authenticate_user:135 - 密码错误
|
||||
2026-09-20 18:30:16.941 | | | | 93497-f06833 | WARNING | module_admin.service.login_service:authenticate_user:133 - 密码错误
|
||||
2026-09-20 18:30:31.597 | | | | 93971-584c3b | WARNING | module_admin.service.login_service:authenticate_user:133 - 密码错误
|
||||
2026-09-20 18:30:56.162 | | | | 94357-625365 | WARNING | module_admin.service.login_service:authenticate_user:133 - 密码错误
|
||||
2026-09-20 18:59:56.301 | | | | 16082-90d8eb | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-652' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.134 seconds
|
||||
2026-09-20 20:59:08.538 | | | | 94089-caeb0d | WARNING | config.get_scheduler:on_lock_lost:273 - ⚠️ Worker 94089-caeb0d 失去 Application 锁
|
||||
2026-09-20 21:25:50.212 | cb2c7654e5b94cde929029eaf4b267ee | 6fe2f1d9479d4b3c8a77a4f50f788af9 | 69c6ef438e3f45e298848ceef5c322c5 | 94089-caeb0d | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
2026-09-21 18:40:56.930 | | | | 61855-bf03aa | ERROR | sqlalchemy.pool.base:_close_connection:376 - Exception closing connection <AdaptedConnection <asyncmy.connection.Connection object at 0x116e83310>>
|
||||
Traceback (most recent call last):
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/pool/base.py", line 374, in _close_connection
|
||||
self._dialect.do_close(connection)
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 721, in do_close
|
||||
dbapi_connection.close()
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/asyncmy.py", line 114, in close
|
||||
self.await_(self._connection.ensure_closed())
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
|
||||
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
|
||||
value = await result
|
||||
File "asyncmy/connection.pyx", line 367, in ensure_closed
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/streams.py", line 343, in wait_closed
|
||||
await self._protocol._get_close_waiter(self)
|
||||
BrokenPipeError: [Errno 32] Broken pipe
|
||||
2026-09-21 18:40:57.080 | | | | 61855-bf03aa | ERROR | sqlalchemy.pool.base:_close_connection:376 - Exception closing connection <AdaptedConnection <asyncmy.connection.Connection object at 0x116c77e80>>
|
||||
Traceback (most recent call last):
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/pool/base.py", line 374, in _close_connection
|
||||
self._dialect.do_close(connection)
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 721, in do_close
|
||||
dbapi_connection.close()
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/asyncmy.py", line 114, in close
|
||||
self.await_(self._connection.ensure_closed())
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
|
||||
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
|
||||
value = await result
|
||||
File "asyncmy/connection.pyx", line 367, in ensure_closed
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/streams.py", line 343, in wait_closed
|
||||
await self._protocol._get_close_waiter(self)
|
||||
BrokenPipeError: [Errno 32] Broken pipe
|
||||
@@ -0,0 +1,193 @@
|
||||
2026-09-21 10:13:52.818 | | | | 61855-bf03aa | INFO | server:lifespan:90 - ⏰️ ShiErFeng-FastAPI开始启动
|
||||
2026-09-21 10:13:52.819 | | | | 61855-bf03aa | INFO | utils.common_util:worship:22 -
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// _ooOoo_ //
|
||||
// o8888888o //
|
||||
// 88" . "88 //
|
||||
// (| ^_^ |) //
|
||||
// O\ = /O //
|
||||
// ____/`---'\____ //
|
||||
// .' \\| |// `. //
|
||||
// / \\||| : |||// \ //
|
||||
// / _||||| -:- |||||- \ //
|
||||
// | | \\\ - /// | | //
|
||||
// | \_| ''\---/'' | | //
|
||||
// \ .-\__ `-` ___/-. / //
|
||||
// ___`. .' /--.--\ `. . ___ //
|
||||
// ."" '< `.___\_<|>_/___.' >'"". //
|
||||
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
|
||||
// \ \ `-. \_ __\ /__ _/ .-` / / //
|
||||
// ========`-.____`-.___\_____/___.-`____.-'======== //
|
||||
// `=---=' //
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
|
||||
// 佛祖保佑 永不宕机 永无BUG //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
2026-09-21 10:13:52.821 | | | | 61855-bf03aa | INFO | config.get_db:init_create_table:25 - 🔎 初始化数据库连接...
|
||||
2026-09-21 10:13:52.895 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DATABASE()
|
||||
2026-09-21 10:13:52.895 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.897 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT @@sql_mode
|
||||
2026-09-21 10:13:52.898 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.899 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT @@lower_case_table_names
|
||||
2026-09-21 10:13:52.899 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.901 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - BEGIN (implicit)
|
||||
2026-09-21 10:13:52.901 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_config`
|
||||
2026-09-21 10:13:52.901 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.965 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_dict_type`
|
||||
2026-09-21 10:13:52.965 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.969 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_dict_data`
|
||||
2026-09-21 10:13:52.969 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.971 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_job`
|
||||
2026-09-21 10:13:52.972 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.973 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_job_log`
|
||||
2026-09-21 10:13:52.973 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.976 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_logininfor`
|
||||
2026-09-21 10:13:52.976 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.976 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_oper_log`
|
||||
2026-09-21 10:13:52.977 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.978 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_dept`
|
||||
2026-09-21 10:13:52.978 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.984 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_user`
|
||||
2026-09-21 10:13:52.984 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.986 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_user_role`
|
||||
2026-09-21 10:13:52.986 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.987 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_user_post`
|
||||
2026-09-21 10:13:52.987 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.988 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_menu`
|
||||
2026-09-21 10:13:52.989 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.991 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_post`
|
||||
2026-09-21 10:13:52.991 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.992 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_role`
|
||||
2026-09-21 10:13:52.992 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.994 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_role_dept`
|
||||
2026-09-21 10:13:52.994 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.995 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_role_menu`
|
||||
2026-09-21 10:13:52.995 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.997 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`sys_notice`
|
||||
2026-09-21 10:13:52.997 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.997 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_strain`
|
||||
2026-09-21 10:13:52.998 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:52.999 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_warehouse`
|
||||
2026-09-21 10:13:52.999 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.001 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_customer`
|
||||
2026-09-21 10:13:53.001 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.002 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_customer_site`
|
||||
2026-09-21 10:13:53.002 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.003 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_production_line`
|
||||
2026-09-21 10:13:53.003 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.004 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_pallet`
|
||||
2026-09-21 10:13:53.004 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.005 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_user_warehouse`
|
||||
2026-09-21 10:13:53.005 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.006 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_user_site`
|
||||
2026-09-21 10:13:53.006 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.007 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_inbound_order`
|
||||
2026-09-21 10:13:53.007 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.008 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_inbound_item`
|
||||
2026-09-21 10:13:53.008 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.009 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_filling_record`
|
||||
2026-09-21 10:13:53.009 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.011 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_shipment_order`
|
||||
2026-09-21 10:13:53.011 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.013 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_shipment_item`
|
||||
2026-09-21 10:13:53.013 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.014 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_usage_record`
|
||||
2026-09-21 10:13:53.015 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.016 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_return_order`
|
||||
2026-09-21 10:13:53.016 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.017 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_return_item`
|
||||
2026-09-21 10:13:53.017 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.019 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_pallet_inventory`
|
||||
2026-09-21 10:13:53.019 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.020 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`biz_pallet_event`
|
||||
2026-09-21 10:13:53.020 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.022 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`gen_table`
|
||||
2026-09-21 10:13:53.022 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.023 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - DESCRIBE `shierfeng-fastapi`.`gen_table_column`
|
||||
2026-09-21 10:13:53.023 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [raw sql] ()
|
||||
2026-09-21 10:13:53.024 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - COMMIT
|
||||
2026-09-21 10:13:53.025 | | | | 61855-bf03aa | INFO | config.get_db:init_create_table:28 - ✅️ 数据库连接成功
|
||||
2026-09-21 10:13:53.025 | | | | 61855-bf03aa | INFO | config.get_redis:check_redis_connection:57 - 🔎 开始连接redis...
|
||||
2026-09-21 10:13:53.027 | | | | 61855-bf03aa | INFO | config.get_redis:check_redis_connection:63 - ✅️ redis连接成功
|
||||
2026-09-21 10:13:53.031 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - BEGIN (implicit)
|
||||
2026-09-21 10:13:53.050 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT sys_dict_type.dict_id, sys_dict_type.dict_name, sys_dict_type.dict_type, sys_dict_type.status, sys_dict_type.create_by, sys_dict_type.create_time, sys_dict_type.update_by, sys_dict_type.update_time, sys_dict_type.remark
|
||||
FROM sys_dict_type
|
||||
2026-09-21 10:13:53.050 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [generated in 0.00032s] ()
|
||||
2026-09-21 10:13:53.058 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.058 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [generated in 0.00030s] ('0', 'sys_user_sex', '0')
|
||||
2026-09-21 10:13:53.062 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.063 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.004945s ago] ('0', 'sys_show_hide', '0')
|
||||
2026-09-21 10:13:53.065 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.066 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.007828s ago] ('0', 'sys_normal_disable', '0')
|
||||
2026-09-21 10:13:53.071 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.071 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.01375s ago] ('0', 'sys_job_status', '0')
|
||||
2026-09-21 10:13:53.074 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.074 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.01605s ago] ('0', 'sys_job_group', '0')
|
||||
2026-09-21 10:13:53.079 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.079 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.02133s ago] ('0', 'sys_job_executor', '0')
|
||||
2026-09-21 10:13:53.082 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.082 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.02433s ago] ('0', 'sys_yes_no', '0')
|
||||
2026-09-21 10:13:53.084 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.084 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.02646s ago] ('0', 'sys_notice_type', '0')
|
||||
2026-09-21 10:13:53.087 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.087 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.02908s ago] ('0', 'sys_notice_status', '0')
|
||||
2026-09-21 10:13:53.090 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.090 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.03221s ago] ('0', 'sys_oper_type', '0')
|
||||
2026-09-21 10:13:53.093 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_dict_data.dict_code, sys_dict_data.dict_sort, sys_dict_data.dict_label, sys_dict_data.dict_value, sys_dict_data.dict_type, sys_dict_data.css_class, sys_dict_data.list_class, sys_dict_data.is_default, sys_dict_data.status, sys_dict_data.create_by, sys_dict_data.create_time, sys_dict_data.update_by, sys_dict_data.update_time, sys_dict_data.remark
|
||||
FROM sys_dict_type LEFT OUTER JOIN sys_dict_data ON sys_dict_type.dict_type = sys_dict_data.dict_type AND sys_dict_data.status = %s
|
||||
WHERE sys_dict_type.dict_type = %s AND sys_dict_type.status = %s ORDER BY sys_dict_data.dict_sort
|
||||
2026-09-21 10:13:53.094 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [cached since 0.03604s ago] ('0', 'sys_common_status', '0')
|
||||
2026-09-21 10:13:53.096 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - ROLLBACK
|
||||
2026-09-21 10:13:53.101 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - BEGIN (implicit)
|
||||
2026-09-21 10:13:53.102 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - SELECT DISTINCT sys_config.config_id, sys_config.config_name, sys_config.config_key, sys_config.config_value, sys_config.config_type, sys_config.create_by, sys_config.create_time, sys_config.update_by, sys_config.update_time, sys_config.remark
|
||||
FROM sys_config
|
||||
WHERE true = 1 ORDER BY sys_config.config_id
|
||||
2026-09-21 10:13:53.102 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - [generated in 0.00022s] ()
|
||||
2026-09-21 10:13:53.111 | | | | 61855-bf03aa | INFO | sqlalchemy.log:log:210 - ROLLBACK
|
||||
2026-09-21 10:13:53.112 | | | | 61855-bf03aa | INFO | config.get_scheduler:init_system_scheduler:210 - 🔎 Worker 61855-bf03aa 尝试获取 Application 锁...
|
||||
2026-09-21 10:13:53.114 | | | | 61855-bf03aa | INFO | config.get_scheduler:_start_scheduler_as_leader:235 - 🎯 Worker 61855-bf03aa 持有 Application 锁,开始启动定时任务...
|
||||
2026-09-21 10:13:53.142 | | | | 61855-bf03aa | INFO | apscheduler.schedulers.base:start:214 - Scheduler started
|
||||
2026-09-21 10:13:53.253 | | | | 61855-bf03aa | INFO | config.get_scheduler:_start_scheduler_as_leader:261 - ✅️ 系统初始定时任务加载成功
|
||||
2026-09-21 10:13:53.755 | | | | 61855-bf03aa | INFO | server:lifespan:103 - 🚀 ShiErFeng-FastAPI启动成功
|
||||
2026-09-21 10:13:53.759 | | | | 61855-bf03aa | INFO | server:lifespan:115 - 💻 应用地址:
|
||||
🏠 Local: http://127.0.0.1:9099
|
||||
📡 Network: http://172.16.146.99:9099
|
||||
📡 Network: http://172.16.142.109:9099
|
||||
📡 Network: http://172.16.224.1:9099
|
||||
📡 Network: http://192.168.216.1:9099
|
||||
2026-09-21 10:13:53.759 | | | | 61855-bf03aa | INFO | server:lifespan:122 - 📄 Swagger文档:
|
||||
🏠 Local: http://127.0.0.1:9099/docs
|
||||
📡 Network: http://172.16.146.99:9099/docs
|
||||
📡 Network: http://172.16.142.109:9099/docs
|
||||
📡 Network: http://172.16.224.1:9099/docs
|
||||
📡 Network: http://192.168.216.1:9099/docs
|
||||
2026-09-21 10:13:53.759 | | | | 61855-bf03aa | INFO | server:lifespan:129 - 📚 ReDoc文档:
|
||||
🏠 Local: http://127.0.0.1:9099/redoc
|
||||
📡 Network: http://172.16.146.99:9099/redoc
|
||||
📡 Network: http://172.16.142.109:9099/redoc
|
||||
📡 Network: http://172.16.224.1:9099/redoc
|
||||
📡 Network: http://192.168.216.1:9099/redoc
|
||||
2026-09-21 18:40:56.795 | | | | 61855-bf03aa | INFO | config.get_redis:close_redis_pool:85 - ✅️ 关闭redis连接成功
|
||||
2026-09-21 18:40:57.041 | | | | 61855-bf03aa | INFO | config.get_scheduler:close_system_scheduler:768 - ✅️ 关闭定时任务成功
|
||||
2026-09-21 18:40:57.051 | | | | 61855-bf03aa | INFO | apscheduler.schedulers.base:shutdown:245 - Scheduler has been shut down
|
||||
2026-09-21 18:40:57.077 | | | | 61855-bf03aa | INFO | config.get_scheduler:close_system_scheduler:774 - 🔓 Worker 61855-bf03aa 释放 Application 锁
|
||||
@@ -0,0 +1,865 @@
|
||||
2026-09-22 10:18:42.014 | c67f56d9e06b4ff9b0df0307b310acf1 | e7bf03f3985b4cd1a17c53316ccc1c57 | 16a71940dde04f61b06084ef747adb35 | 76540-54b2cc | WARNING | common.annotation.log_annotation:wrapper:194 - 存在下级部门,不允许删除
|
||||
2026-09-22 10:19:35.435 | ebc19a67e42a40ec9174d89fe47588f9 | 3d7efb2b710d4595b0de10b34865f905 | 4250cfd7ec8e42a2ad031f9b8a643a79 | 76540-54b2cc | WARNING | common.annotation.log_annotation:wrapper:194 - 部门存在用户,不允许删除
|
||||
2026-09-22 10:19:41.831 | f8c6319564384ee9a8959686a5b1bc19 | 6a2dc693410d4cb788b16f9d4f95056c | 984b4a1ddd574fdbaf75b54ea37fb207 | 76540-54b2cc | WARNING | common.annotation.log_annotation:wrapper:194 - 部门存在用户,不允许删除
|
||||
2026-09-22 11:08:34.493 | 75f92b597fe6486d9f9c68073eedff31 | 1984201c0f604cf38bc4167d3d8de2bf | c14edb9c8ce84ad4a4bb9000b0ff96e3 | 76540-54b2cc | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-22 13:09:15.676 | 90cdba067fdc471393e87d456603cf92 | 1cf6c85fa12f4f52af203f8e4eff32bf | 566753bee664495580fa822c1f551794 | 76540-54b2cc | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-22 13:42:34.457 | | | | 86619-9b6b60 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-964' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.110 seconds
|
||||
2026-09-22 18:11:19.770 | | | | 12973-c0e2e4 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.143 seconds
|
||||
2026-09-22 18:32:32.062 | | | | 35168-f5f084 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.105 seconds
|
||||
2026-09-22 19:27:26.746 | | | | 57152-6a64c1 | WARNING | config.get_scheduler:on_lock_lost:273 - ⚠️ Worker 57152-6a64c1 失去 Application 锁
|
||||
2026-09-22 22:24:38.614 | | | | 24817-caa44a | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.118 seconds
|
||||
2026-09-22 22:24:38.636 | | | | 24819-8b91d3 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.109 seconds
|
||||
2026-09-22 22:24:38.728 | | | | 24821-323481 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.177 seconds
|
||||
2026-09-22 22:24:38.827 | | | | 24820-cd85d4 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-19' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.172 seconds
|
||||
2026-09-22 22:24:38.888 | | | | 24818-25445c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-19' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.250 seconds
|
||||
2026-09-22 22:52:36.300 | | | | 61048-169acd | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.102 seconds
|
||||
2026-09-22 23:10:59.657 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-1' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 2.626 seconds
|
||||
2026-09-22 23:11:09.832 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 9.292 seconds
|
||||
2026-09-22 23:11:10.264 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-7' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.235 seconds
|
||||
2026-09-22 23:11:10.486 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.129 seconds
|
||||
2026-09-22 23:11:11.481 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-16' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.580 seconds
|
||||
2026-09-22 23:11:12.091 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-19' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.259 seconds
|
||||
2026-09-22 23:11:13.035 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-22' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.856 seconds
|
||||
2026-09-22 23:11:13.520 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-28' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.141 seconds
|
||||
2026-09-22 23:11:14.010 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-31' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.185 seconds
|
||||
2026-09-22 23:11:16.736 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-43' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.183 seconds
|
||||
2026-09-22 23:11:17.795 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-61' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.250 seconds
|
||||
2026-09-22 23:11:18.541 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-64' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.124 seconds
|
||||
2026-09-22 23:11:19.391 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-67' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.514 seconds
|
||||
2026-09-22 23:11:19.736 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-70' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.231 seconds
|
||||
2026-09-22 23:11:20.331 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-73' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.501 seconds
|
||||
2026-09-22 23:11:20.756 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-76' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.374 seconds
|
||||
2026-09-22 23:11:21.218 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task finished name='Task-83' coro=<Queue.join() done, defined at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/queues.py:207> result=None created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/tasks.py:636> took 0.124 seconds
|
||||
2026-09-22 23:11:21.631 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-88' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.214 seconds
|
||||
2026-09-22 23:11:21.804 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task finished name='Task-90' coro=<BaseEventLoop.shutdown_default_executor() done, defined at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:560> result=None created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/tasks.py:636> took 0.136 seconds
|
||||
2026-09-22 23:11:22.566 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-97' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.228 seconds
|
||||
2026-09-22 23:11:23.001 | | | | 78383-c16ef2 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-100' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.185 seconds
|
||||
2026-09-22 23:16:20.261 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-1' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.530 seconds
|
||||
2026-09-22 23:16:23.668 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 3.320 seconds
|
||||
2026-09-22 23:16:23.843 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-7' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.133 seconds
|
||||
2026-09-22 23:16:24.175 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.143 seconds
|
||||
2026-09-22 23:16:24.731 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-16' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.293 seconds
|
||||
2026-09-22 23:16:25.522 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-22' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.408 seconds
|
||||
2026-09-22 23:16:26.308 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-31' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.385 seconds
|
||||
2026-09-22 23:16:26.748 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-34' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.308 seconds
|
||||
2026-09-22 23:16:26.940 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-37' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.121 seconds
|
||||
2026-09-22 23:16:29.047 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-46' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.126 seconds
|
||||
2026-09-22 23:16:29.532 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-49' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.331 seconds
|
||||
2026-09-22 23:16:29.798 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-52' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.227 seconds
|
||||
2026-09-22 23:16:30.739 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-67' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.550 seconds
|
||||
2026-09-22 23:16:31.033 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-70' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.274 seconds
|
||||
2026-09-22 23:16:31.477 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-73' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.384 seconds
|
||||
2026-09-22 23:16:32.285 | | | | 82428-c58b46 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-76' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.432 seconds
|
||||
2026-09-22 23:25:04.359 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-1' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.418 seconds
|
||||
2026-09-22 23:25:06.115 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 1.584 seconds
|
||||
2026-09-22 23:25:06.804 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-7' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.361 seconds
|
||||
2026-09-22 23:25:08.052 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.753 seconds
|
||||
2026-09-22 23:25:08.726 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-13' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.494 seconds
|
||||
2026-09-22 23:25:09.823 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-16' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.894 seconds
|
||||
2026-09-22 23:25:10.286 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-28' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.128 seconds
|
||||
2026-09-22 23:25:11.132 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-34' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.243 seconds
|
||||
2026-09-22 23:25:11.455 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-37' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.173 seconds
|
||||
2026-09-22 23:25:11.868 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-40' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.294 seconds
|
||||
2026-09-22 23:25:12.118 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-40' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.132 seconds
|
||||
2026-09-22 23:25:14.498 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-43' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.285 seconds
|
||||
2026-09-22 23:25:14.974 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-52' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.149 seconds
|
||||
2026-09-22 23:25:15.334 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-55' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.110 seconds
|
||||
2026-09-22 23:25:16.807 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-67' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.847 seconds
|
||||
2026-09-22 23:25:17.285 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-70' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.196 seconds
|
||||
2026-09-22 23:25:18.869 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-73' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 1.346 seconds
|
||||
2026-09-22 23:25:19.708 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-76' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.444 seconds
|
||||
2026-09-22 23:25:19.920 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-79' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.110 seconds
|
||||
2026-09-22 23:25:20.493 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-82' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.347 seconds
|
||||
2026-09-22 23:25:21.245 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-94' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.195 seconds
|
||||
2026-09-22 23:25:21.859 | | | | 89690-6ac0a0 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-100' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.277 seconds
|
||||
2026-09-22 23:29:25.904 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-1' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.632 seconds
|
||||
2026-09-22 23:29:27.988 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 1.939 seconds
|
||||
2026-09-22 23:29:28.432 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.114 seconds
|
||||
2026-09-22 23:29:28.753 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-13' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.238 seconds
|
||||
2026-09-22 23:29:29.126 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-16' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.270 seconds
|
||||
2026-09-22 23:29:29.634 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-22' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.332 seconds
|
||||
2026-09-22 23:29:30.338 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-25' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.607 seconds
|
||||
2026-09-22 23:29:30.898 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-28' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.103 seconds
|
||||
2026-09-22 23:29:31.035 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task finished name='Task-29' coro=<Queue.join() done, defined at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/queues.py:207> result=None created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/tasks.py:636> took 0.109 seconds
|
||||
2026-09-22 23:29:31.424 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-31' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.206 seconds
|
||||
2026-09-22 23:29:31.628 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-34' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.176 seconds
|
||||
2026-09-22 23:29:32.035 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-34' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.108 seconds
|
||||
2026-09-22 23:29:32.521 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-34' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.359 seconds
|
||||
2026-09-22 23:29:32.865 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-37' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.137 seconds
|
||||
2026-09-22 23:29:33.269 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-37' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.172 seconds
|
||||
2026-09-22 23:29:33.756 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-40' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.319 seconds
|
||||
2026-09-22 23:29:36.375 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-43' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.355 seconds
|
||||
2026-09-22 23:29:36.876 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task finished name='Task-45' coro=<BaseEventLoop.shutdown_default_executor() done, defined at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:560> result=None created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/tasks.py:636> took 0.119 seconds
|
||||
2026-09-22 23:29:37.417 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-49' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.197 seconds
|
||||
2026-09-22 23:29:37.693 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-52' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.243 seconds
|
||||
2026-09-22 23:29:38.113 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-55' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.146 seconds
|
||||
2026-09-22 23:29:38.457 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-58' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.111 seconds
|
||||
2026-09-22 23:29:38.835 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-64' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.180 seconds
|
||||
2026-09-22 23:29:39.085 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-67' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.190 seconds
|
||||
2026-09-22 23:29:39.817 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-70' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.421 seconds
|
||||
2026-09-22 23:29:40.668 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-73' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.836 seconds
|
||||
2026-09-22 23:29:40.874 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Handle _run_until_complete_cb(<Future finis...events.py:429>) at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:184 created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:103> took 0.105 seconds
|
||||
2026-09-22 23:29:41.871 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-76' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.329 seconds
|
||||
2026-09-22 23:29:42.082 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Handle _run_until_complete_cb(<Task finishe.../tasks.py:636>) at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:184 created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/events.py:80> took 0.172 seconds
|
||||
2026-09-22 23:29:42.258 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-79' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.152 seconds
|
||||
2026-09-22 23:29:43.444 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-85' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.621 seconds
|
||||
2026-09-22 23:29:44.050 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-91' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.344 seconds
|
||||
2026-09-22 23:29:44.843 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-97' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.610 seconds
|
||||
2026-09-22 23:29:45.132 | | | | 92873-630804 | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-100' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.175 seconds
|
||||
2026-09-22 23:33:02.354 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-1' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.154 seconds
|
||||
2026-09-22 23:33:02.683 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-4' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.167 seconds
|
||||
2026-09-22 23:33:03.250 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-7' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.350 seconds
|
||||
2026-09-22 23:33:06.726 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 3.057 seconds
|
||||
2026-09-22 23:33:06.969 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-13' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.198 seconds
|
||||
2026-09-22 23:33:07.345 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-16' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.319 seconds
|
||||
2026-09-22 23:33:08.435 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-19' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 1.044 seconds
|
||||
2026-09-22 23:33:09.165 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-22' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.627 seconds
|
||||
2026-09-22 23:33:09.402 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-25' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.193 seconds
|
||||
2026-09-22 23:33:09.847 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-28' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.392 seconds
|
||||
2026-09-22 23:33:10.270 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-31' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.277 seconds
|
||||
2026-09-22 23:33:14.225 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-40' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.313 seconds
|
||||
2026-09-22 23:33:14.518 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-43' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.231 seconds
|
||||
2026-09-22 23:33:14.899 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-46' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.145 seconds
|
||||
2026-09-22 23:33:15.434 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-49' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.337 seconds
|
||||
2026-09-22 23:33:15.651 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-52' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.135 seconds
|
||||
2026-09-22 23:33:16.085 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-52' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.347 seconds
|
||||
2026-09-22 23:33:16.906 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-55' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.604 seconds
|
||||
2026-09-22 23:33:17.133 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-55' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.116 seconds
|
||||
2026-09-22 23:33:17.304 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-58' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.108 seconds
|
||||
2026-09-22 23:33:17.742 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-58' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.407 seconds
|
||||
2026-09-22 23:33:18.396 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-61' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.501 seconds
|
||||
2026-09-22 23:33:18.668 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-64' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.175 seconds
|
||||
2026-09-22 23:33:21.458 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-67' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 2.651 seconds
|
||||
2026-09-22 23:33:21.857 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-70' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.249 seconds
|
||||
2026-09-22 23:33:22.116 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-76' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.123 seconds
|
||||
2026-09-22 23:33:23.120 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-97' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.134 seconds
|
||||
2026-09-22 23:33:23.589 | | | | 95504-09fcce | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-109' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.124 seconds
|
||||
2026-09-22 23:36:23.014 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-1' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.116 seconds
|
||||
2026-09-22 23:36:24.009 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-10' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.716 seconds
|
||||
2026-09-22 23:36:24.730 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-13' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.417 seconds
|
||||
2026-09-22 23:36:25.937 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-16' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.938 seconds
|
||||
2026-09-22 23:36:28.981 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-19' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 2.588 seconds
|
||||
2026-09-22 23:36:29.927 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-22' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.554 seconds
|
||||
2026-09-22 23:36:30.572 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-25' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.514 seconds
|
||||
2026-09-22 23:36:31.068 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-28' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.207 seconds
|
||||
2026-09-22 23:36:31.776 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-31' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.128 seconds
|
||||
2026-09-22 23:36:35.399 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-37' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.587 seconds
|
||||
2026-09-22 23:36:36.087 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-40' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.317 seconds
|
||||
2026-09-22 23:36:37.078 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-43' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.785 seconds
|
||||
2026-09-22 23:36:37.558 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-46' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.296 seconds
|
||||
2026-09-22 23:36:38.141 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-52' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.220 seconds
|
||||
2026-09-22 23:36:38.527 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-55' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.271 seconds
|
||||
2026-09-22 23:36:39.076 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-58' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.468 seconds
|
||||
2026-09-22 23:36:40.540 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-61' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.841 seconds
|
||||
2026-09-22 23:36:40.842 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-64' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.249 seconds
|
||||
2026-09-22 23:36:46.647 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-67' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 4.703 seconds
|
||||
2026-09-22 23:36:47.157 | | | | 98313-a0269c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-79' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.108 seconds
|
||||
2026-09-22 23:46:00.259 | | | | 12467-333dad | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-46' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.100 seconds
|
||||
2026-09-22 23:47:35.249 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Connection closed by server.
|
||||
2026-09-22 23:47:36.377 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:37.380 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:38.384 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:39.386 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:40.391 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:41.395 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:42.401 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:43.405 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:44.409 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:45.410 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:46.412 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:47.413 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:48.416 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:49.418 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:50.421 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:51.425 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:52.430 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:53.433 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:54.435 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:55.438 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:56.440 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:57.451 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:58.456 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:47:59.458 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:00.461 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:01.464 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:02.467 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:03.470 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:04.474 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:05.481 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:06.489 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:07.493 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:08.500 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:09.504 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:10.506 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:11.512 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:12.518 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:13.521 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:14.526 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:15.531 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:16.534 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:17.539 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:18.545 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:19.566 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:20.573 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:21.593 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:22.599 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:23.603 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:24.606 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:25.610 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:26.613 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:27.620 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:28.625 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:29.628 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:30.632 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:31.636 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:32.640 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:33.645 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:34.648 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:35.651 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:36.655 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:37.658 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:38.663 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:39.666 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:40.686 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:41.697 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:42.699 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:43.702 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:44.705 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:45.707 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:46.710 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:47.713 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:48.718 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:49.723 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:50.728 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:51.737 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:52.750 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:53.757 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:54.763 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:55.766 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:56.770 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:57.774 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:58.778 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:48:59.780 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:00.785 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:01.788 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:02.790 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:03.794 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:04.797 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:05.800 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:06.804 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:07.809 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:08.811 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:09.815 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:10.818 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:11.822 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:12.824 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:13.827 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:14.829 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:15.831 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:16.834 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:17.838 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:18.841 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:19.845 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:20.848 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:21.851 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:22.855 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:23.859 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:24.863 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:25.867 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:26.871 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:27.875 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:28.879 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:29.882 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:30.887 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:31.889 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:32.892 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:33.896 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:34.901 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:35.907 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:36.912 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:37.915 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:38.922 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:40.026 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:41.030 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:42.064 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:43.094 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:44.123 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:45.201 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:46.251 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:47.265 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:48.411 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:49.416 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:50.421 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:51.441 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:52.485 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:53.488 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:54.491 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:55.536 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:56.543 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:57.548 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:58.552 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:49:59.556 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:00.563 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:01.569 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:02.576 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:03.757 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:04.780 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:05.786 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:06.792 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:07.798 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:08.805 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:09.811 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:10.816 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:11.823 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:12.832 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:13.835 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:14.841 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:15.847 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:16.851 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:17.856 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:18.861 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:19.866 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:20.873 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:21.880 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:22.889 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:23.897 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:24.904 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:25.911 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:26.916 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:27.920 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:28.925 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:29.929 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:30.934 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:31.937 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:32.941 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:33.945 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:34.953 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:35.959 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:36.965 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:37.972 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:38.978 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:39.981 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:40.986 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:41.989 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:42.992 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:43.995 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:44.999 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:46.002 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:47.005 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:48.008 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:49.012 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:50.015 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:51.019 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:52.021 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:53.024 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:54.028 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:55.029 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:56.032 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:57.036 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:58.038 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:50:59.041 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:51:00.045 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:51:01.047 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:51:02.049 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-22 23:51:03.051 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:03.571 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:04.574 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:05.579 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:06.583 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:07.587 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:08.591 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:09.595 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:10.602 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:11.608 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:12.613 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:13.614 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:14.617 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:15.621 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:16.623 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:00:17.625 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:11.971 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:13.078 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:14.081 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:15.085 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:16.088 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:17.092 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:18.092 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:19.095 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:20.098 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:21.100 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:22.113 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:23.116 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:11:24.117 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:27:40.574 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:27:41.686 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:27:42.690 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:27:43.694 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:27:44.698 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:44:00.591 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:44:01.596 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:44:02.600 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:44:03.604 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:44:04.607 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:44:05.609 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:03.587 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:04.590 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:05.594 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:06.597 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:07.598 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:08.600 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:09.603 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:10.606 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:11.609 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:12.611 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:13.615 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:14.635 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:15.647 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:16.650 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:17.651 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:18.654 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:19.657 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:20.658 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:21.661 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:22.662 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:23.665 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:24.668 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:25.670 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:26.672 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:27.674 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:28.677 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:29.681 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 00:50:30.684 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:05:50.591 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:05:51.593 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:05:52.595 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:05:53.599 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:05:54.601 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:05:55.603 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:23:03.608 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:23:04.611 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:23:05.614 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:23:06.618 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:23:07.620 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:23:08.623 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:40:49.623 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:40:50.710 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:40:51.712 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:40:52.716 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:40:53.718 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:40:54.721 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:51:03.636 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:51:04.641 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:51:05.645 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:51:06.649 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:51:07.653 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:51:08.658 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 01:51:09.661 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:06:48.621 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:06:49.722 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:06:50.726 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:06:51.730 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:06:52.736 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:23:48.707 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:23:49.725 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:23:50.730 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:23:51.734 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:23:52.739 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:23:53.743 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:39:38.635 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:39:39.642 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:39:40.647 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:39:41.652 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:39:42.655 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:39:43.658 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:56:25.666 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:56:26.755 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:56:27.759 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:56:28.763 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:56:29.767 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 02:56:30.770 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:12:53.660 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:12:54.759 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:12:55.762 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:12:56.766 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:12:57.769 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:12:58.777 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:20.670 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:21.673 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:22.677 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:23.681 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:24.685 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:25.688 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:26.691 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:14:27.692 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:31:33.664 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:31:34.670 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:31:35.675 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:31:36.679 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:31:37.682 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:31:38.687 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:48:22.670 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:48:23.764 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:48:24.767 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:48:25.771 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:48:26.773 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 03:48:27.777 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:05:24.673 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:05:25.677 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:05:26.680 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:05:27.683 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:05:28.685 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:05:29.689 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:23:12.688 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:23:13.693 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:23:14.696 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:23:15.698 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:23:16.700 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:23:17.702 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:40:37.692 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:40:38.697 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:40:39.703 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:40:40.707 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:40:41.710 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:40:42.715 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:55:41.727 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:55:42.800 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:55:43.806 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:55:44.812 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:55:45.816 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 04:55:46.821 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:13:12.718 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:13:13.807 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:13:14.811 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:13:15.817 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:13:16.823 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:13:17.826 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:29:29.715 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:29:30.719 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:29:31.723 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:29:32.726 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:29:33.730 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:29:34.732 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:05.738 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:06.819 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:07.822 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:08.826 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:09.829 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:10.833 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:11.835 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 05:45:12.839 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:01:29.723 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:01:30.824 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:01:31.826 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:01:32.829 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:01:33.832 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:18:33.830 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:18:34.835 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:18:35.837 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:18:36.839 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:18:37.843 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:18:38.846 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:36:06.836 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:36:07.839 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:36:08.842 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:36:09.845 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:36:10.848 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:51:09.750 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:51:10.754 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:51:11.758 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:51:12.761 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:51:13.764 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 06:51:14.768 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:07:09.760 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:07:10.764 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:07:11.765 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:07:12.769 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:07:13.771 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:07:14.774 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:24:44.764 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:24:45.767 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:24:46.769 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:24:47.771 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:24:48.776 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:24:49.781 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:39:47.786 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:39:48.807 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:39:49.809 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:39:50.812 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:39:51.815 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:39:52.819 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:05.774 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:06.780 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:07.785 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:08.790 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:09.793 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:10.796 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:11.799 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:12.802 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:46:13.805 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:00.778 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:01.872 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:02.875 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:03.877 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:04.880 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:05.882 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:06.884 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:07.886 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:08.887 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:09.889 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:10.895 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:11.900 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:12.910 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:55:13.921 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:10.286 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:11.430 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:12.438 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:13.450 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:14.457 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:15.603 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:16.659 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:17.666 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:18.673 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:19.681 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:20.683 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:21.689 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:22.696 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:23.698 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:24.700 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:25.702 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:26.704 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:27.708 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:28.711 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:29.713 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:30.716 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:31.718 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:32.720 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:33.722 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:34.726 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:35.758 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:36.794 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:37.811 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:38.818 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:39.823 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:40.829 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:41.836 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:42.883 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:43.890 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:44.909 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:45.918 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:46.924 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:47.936 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:48.941 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:49.949 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:50.955 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:51.977 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:52.991 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:53.998 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:55.014 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:56.022 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:57.029 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:58.037 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:58:59.053 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:00.058 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:01.071 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:02.082 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:03.085 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:04.091 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:05.109 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:06.112 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:07.120 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:08.123 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:09.129 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:10.135 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:11.139 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:12.143 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:13.150 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:14.157 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:15.168 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:16.176 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:17.202 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:18.232 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:19.245 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:20.254 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:21.262 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:22.269 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:23.274 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:24.280 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:25.285 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:26.293 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:27.298 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:28.303 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:29.310 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:30.317 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:31.324 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:32.332 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:33.337 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:34.342 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:35.349 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:36.353 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:37.359 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:38.364 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:39.373 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:40.377 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:41.382 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:42.389 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:43.394 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:44.402 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:45.410 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:46.419 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:47.425 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:48.431 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:49.435 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:50.454 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:51.459 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:52.463 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:53.472 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:54.480 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:55.486 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:56.491 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:57.497 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:58.509 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 07:59:59.520 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:00.526 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:01.531 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:02.537 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:03.548 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:04.555 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:05.563 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:06.578 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:07.584 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:08.591 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:09.597 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:10.601 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:11.606 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:12.609 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:13.613 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:14.618 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:15.622 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:16.628 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:17.632 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:18.640 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:19.645 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:20.648 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:21.651 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:22.654 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:23.655 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:24.660 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:25.667 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:26.672 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:27.676 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:28.679 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:29.681 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:30.683 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:31.687 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:32.690 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:33.693 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:34.697 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:35.699 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:36.701 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:37.704 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:38.708 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:39.710 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:40.713 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:41.722 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:42.729 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:43.732 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:44.737 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:45.740 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:46.744 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:47.747 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:48.751 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:49.754 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:50.757 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:51.761 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:52.764 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:53.768 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:54.772 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:55.776 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:56.785 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:57.791 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:58.797 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:00:59.805 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:00.813 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:01.839 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:02.853 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:03.861 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:04.865 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:05.868 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:06.870 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:07.871 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:08.874 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:09.880 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:10.882 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:11.883 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:12.885 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:13.888 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:14.890 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:15.893 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:16.896 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:17.898 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:18.900 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:19.905 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:20.911 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:21.919 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:22.928 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:23.931 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:24.935 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:25.940 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:26.949 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:27.955 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:28.963 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:29.969 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:30.975 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:31.980 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:32.987 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:34.000 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:35.010 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:36.013 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:37.017 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:38.019 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:39.022 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:40.027 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:41.031 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:42.036 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:43.038 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:44.040 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:45.044 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:46.046 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:47.048 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:48.052 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:49.055 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:50.057 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:51.059 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:52.061 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:53.064 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:54.068 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:55.071 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:56.074 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:57.077 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:58.081 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:01:59.083 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:00.087 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:01.090 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:02.093 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:03.095 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:04.098 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:05.101 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:06.106 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:07.108 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:08.110 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:09.113 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:10.119 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:11.122 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:12.127 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:13.133 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:14.140 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:15.147 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:16.151 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:17.157 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:18.171 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:19.176 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:20.181 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:21.189 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:22.198 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:23.202 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
2026-09-23 08:02:24.205 | | | | 12367-8e907e | ERROR | module_admin.service.log_service:consume_stream:464 - 日志聚合消费异常: Error 61 connecting to 127.0.0.1:6379. Connection refused.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
2026-09-26 12:55:38.977 | | | | 43890-69016c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-631' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.114 seconds
|
||||
2026-09-26 12:55:41.558 | | | | 43890-69016c | WARNING | asyncio.base_events:_run_once:1904 - Executing <Task pending name='Task-1137' coro=<IsolatedAsyncioTestCase._asyncioLoopRunner() running at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:95> wait_for=<Future pending cb=[Task.task_wakeup()] created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/base_events.py:429> created at /Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/unittest/async_case.py:117> took 0.131 seconds
|
||||
2026-09-26 13:40:25.189 | 4adcaf0fe9734e06a3e3540456d2dcda | bfc317a4ef2d47f9b1ff9b0dacbb4caf | f325961f7b574bb0946772b05e178ded | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:219 - 用户token已失效,请重新登录
|
||||
2026-09-26 16:46:13.947 | 31755c4a7f4747f3963c5b94c040836f | 054f0424f5df44c196e82d4403fe8b1e | 6a70d146cceb42a08e58f1df9d00e30a | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.354 | b5e7bd983c794a9f8bdccfee51db8d73 | 005c76cace20459a8111156d5dfb9a69 | fdf16bf6fb774975978a7ee55994ccef | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.355 | cbf427c3908042d29c47fe1fdf002787 | 76eea226e3914e978fc1945589863eb0 | a036db90caa74c59ad836fcb6dfb4698 | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.358 | badf85531e264537881425cefd532a4e | 9777e8101ca64266a7fc028277cba6b6 | 7ac0e140695745698e8f6182e49f44a0 | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.360 | 0a5c054a6aeb4d5da44cf7edeba90bda | 546e0c5fca214ac59e6b1ca60bb51a5d | b6d578ac982343e780115f406dbd2f80 | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.360 | f9d8cb8d6f13403693533d4831d34fe2 | 85788d53e2c544128a120b7468ce841f | 92f14081de0344f18ba48a801b6a3277 | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.360 | 03ab2322a8a942c0890e0746010f06c6 | a118b2fa8b324ab6ad8ad7b7b8e27279 | b295f1db72c24d8d815aba6908e3239e | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.404 | d541c8c66d304766be61766e9cce2648 | f7a2b5ba8d04420d9fc819298ece8a82 | 7067c57860c24bc6af0047f6f0523112 | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.408 | 0b279fc9e3f245c2a269f42cd15790df | d00aa8f0f30a4eb38350d2d559f2b783 | fbfb19dcf5ab4ab18f9179912242ebba | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.409 | 8f088061d64445eb83100393320bba81 | de6a6a19267e474195c7b3d33334dcc5 | 58249b4d816b4a3b8833981f5aadebca | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 19:22:28.411 | 20c430d179d44806a414717830bf1c44 | 07c1721f324e447a9d121d6e78518182 | 286523eb9ad7456697859a49d3c85381 | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 21:43:11.533 | f639f0f70cf841baa341fff7fda02dbf | c08cd9bcfe1447f9809115a22a51c909 | 4b0d65ad896c4f79b36636218ef526de | 35286-85260c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-26 23:43:55.134 | | | | 35286-85260c | ERROR | sqlalchemy.pool.base:_close_connection:376 - Exception closing connection <AdaptedConnection <asyncmy.connection.Connection object at 0x120ddf370>>
|
||||
Traceback (most recent call last):
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/pool/base.py", line 374, in _close_connection
|
||||
self._dialect.do_close(connection)
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 721, in do_close
|
||||
dbapi_connection.close()
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/asyncmy.py", line 114, in close
|
||||
self.await_(self._connection.ensure_closed())
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
|
||||
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
|
||||
value = await result
|
||||
File "asyncmy/connection.pyx", line 367, in ensure_closed
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/streams.py", line 343, in wait_closed
|
||||
await self._protocol._get_close_waiter(self)
|
||||
BrokenPipeError: [Errno 32] Broken pipe
|
||||
2026-09-27 00:05:01.664 | a9662ac3b2254f2d88ec8edf6e409c8d | 3ced5ce006d845db8dbec0c9accad850 | 7841be0637224ec5b48e2defe6f48baa | 35794-8a000d | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
2026-09-27 01:20:39.570 | aeca06e98b2a457bae7a555ebab9c3e4 | c7b5cc2cd8954f239f72235abb121b38 | 1f860582c3a7413c835d1b2312151742 | 36967-fe3b0d | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 01:30:36.841 | | | | 36967-fe3b0d | WARNING | config.get_scheduler:on_lock_lost:273 - ⚠️ Worker 36967-fe3b0d 失去 Application 锁
|
||||
2026-09-27 09:17:40.878 | efa6272b9713467b980b5e2279502288 | 257ee926db59431baeae83aa56d1fd3d | 7b08ae862c674486a217cbccc2da56ca | 36967-fe3b0d | WARNING | module_admin.service.login_service:__check_login_captcha:167 - 验证码已失效
|
||||
2026-09-27 09:17:40.880 | efa6272b9713467b980b5e2279502288 | 257ee926db59431baeae83aa56d1fd3d | 7b08ae862c674486a217cbccc2da56ca | 36967-fe3b0d | WARNING | common.annotation.log_annotation:wrapper:197 - 验证码已失效
|
||||
2026-09-27 11:09:14.360 | 8b0be0a5114141bdb37b6431382fcdc2 | 4fc3e9a9e9ef4334b746ef8be065c54e | 3515f9adf1e64b50a5ee5c28b522ea42 | 62289-86935c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 11:50:06.114 | 8629920d3544439aa74e32d77b771ea7 | 2b71ec3764424e8595738c4263865922 | c1f4efbd1c8540e4827e7b543f3cf419 | 62289-86935c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 12:05:47.001 | | | | 62289-86935c | WARNING | config.get_scheduler:on_lock_lost:273 - ⚠️ Worker 62289-86935c 失去 Application 锁
|
||||
2026-09-27 12:07:47.946 | 2f3a94e35bfa40749edad4676fc7f829 | f1516386a6f54cffa9cbda8056afdb5f | 0539b6e3453d4245b3cbb995666e0250 | 62289-86935c | WARNING | module_admin.service.login_service:authenticate_user:133 - 密码错误
|
||||
2026-09-27 12:07:47.947 | 2f3a94e35bfa40749edad4676fc7f829 | f1516386a6f54cffa9cbda8056afdb5f | 0539b6e3453d4245b3cbb995666e0250 | 62289-86935c | WARNING | common.annotation.log_annotation:wrapper:197 - 密码错误
|
||||
2026-09-27 12:08:42.318 | f45d2036019848339490a295691a606a | ee76be6f453a45af83ae1f13a19a244e | 92414b750a2949b799c60542464253fc | 62289-86935c | WARNING | module_admin.service.login_service:__check_login_captcha:167 - 验证码已失效
|
||||
2026-09-27 12:08:42.319 | f45d2036019848339490a295691a606a | ee76be6f453a45af83ae1f13a19a244e | 92414b750a2949b799c60542464253fc | 62289-86935c | WARNING | common.annotation.log_annotation:wrapper:197 - 验证码已失效
|
||||
2026-09-27 12:38:43.360 | 93d8f14042c14439a1fc9af9781b3917 | ab93661ba45545d2b98611723aa80c97 | 2d7a40ea30bf4d96ba3f38d98add01d4 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不符合首次入库条件
|
||||
2026-09-27 12:38:51.959 | b647ecabff704381839c93c606148449 | f9860290d57f450f8c47e77082417e96 | ff9ad3724a044fb3b55f5494fe3e0636 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不符合首次入库条件
|
||||
2026-09-27 12:39:40.079 | e35e37edaf3349f2a483325410fb7d08 | c782c7bb104b4505bbf7b7570ed48dcc | e9a1da20ac1e460b919d068fc53f0c5c | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不存在
|
||||
2026-09-27 12:39:55.747 | 8e6c37c17dee4acca97d81bc1c8df234 | 1f1439ce9ae24fd0857a25a6d5247b19 | 833774659e364bf0b953c733be109f02 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不存在
|
||||
2026-09-27 12:40:13.052 | 1b6fe92ad661496db94d6c545c079795 | ee5ec419404a4422b0bf8cabb3359e9e | 205e6790a7034d76a33d5c57597124d3 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不存在
|
||||
2026-09-27 12:41:12.508 | d0dc9c192e974f69884cb4658b93f435 | 4b462f3020ac481ba0a37b88210158f1 | edd50d81cede49cab7bbaf6028207b20 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不符合首次入库条件
|
||||
2026-09-27 12:41:15.159 | 89247b5e1c554337ac85f86a8e0ded6f | 4fe93c98b37943b1b5112297fa9b4ca6 | f32a9908ad73427bb834dcabc162726e | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不符合首次入库条件
|
||||
2026-09-27 12:41:17.335 | e5ee875f98314e67b3fac45dd00855da | 098796711da540b2947eefb29c9870af | 15a558072c3740aba34b002e5000128f | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不符合首次入库条件
|
||||
2026-09-27 12:42:07.755 | 84dde08dd86d45eab607fe861086fd39 | 63af6c1b8c7544808c5ef237972b2f78 | e631e73908cd4d68a8ae53a5a4789d66 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不符合仓库空托盘装填条件
|
||||
2026-09-27 12:43:25.899 | ea8ced2637674cbaaebe945011758d9c | 47742a638abb4382bd3084ba08361579 | 22477243875c4e9da7431c65f3244cd0 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不在仓库
|
||||
2026-09-27 12:43:55.961 | 80c5c99118c74ccf847411f43ff77904 | b550f418bfe3473bb1284d63dc6417ad | e921e40ad7e74ac9afa7e1b50dc1b8b1 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不在仓库
|
||||
2026-09-27 12:44:03.523 | 6e0c2aa14062420eaa30e3e8757d248b | 7fcdba6045554c039f2839d9c3334ddb | df6c3c3149804e879bba5f1dd72d3215 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不在仓库
|
||||
2026-09-27 12:45:06.004 | 071d1550da4e430e81f2829a2ea75518 | c87b19bd407048b998db61c27a7b79bb | b9ec0b3ae8e04b7fa9e2bebb57c63403 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不在仓库
|
||||
2026-09-27 12:48:34.536 | 72cde0024dbd4261bef45f58ef165ed6 | ce5921bbde4d439dad5f27119fe74cde | 05771f45855549ac81165ce682c1ed19 | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不存在活动装填周期
|
||||
2026-09-27 12:48:40.629 | d94f732666cf4f4aa6c8e00b8a43519a | 26272550f15e47b2b8894cd99fad17f6 | d8c14cc3ca0d4b3ea598e77a5b2a5d8e | 98878-57c13e | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不存在活动装填周期
|
||||
2026-09-27 21:03:23.484 | 0d2daf1685da417a86cc18a1f1703671 | 8c9c0df2542c42788c1e3ef786304a34 | 8923f52c36684edf843eee43c241217e | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.309 | aeaaba8686fd4bb0bcfe70ee37a7f99c | 87413968a9c94012bc4d0e1cb387c5ad | 5373a4b28b7548f1a12fe8fe09420c4e | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.315 | b9416504567448838c27351ce695e790 | 6ef0ec202543402bbcbc607eea3dd33f | f40e81e63e7f4f0aace83731a84f1b3d | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.315 | e39e66a972964ed5a16e02b9603c8753 | 9b9124ad9d0349368089d0d4a48732e7 | 75a317beb07f47498325c29e26217446 | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.338 | 43408d4704e340109cd40500cc4a4220 | 3dcbaa0b65d148fd8379a106dd81494a | 73b40a6c8eb44ba69aafe016157a354a | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.340 | 9d4a6be5b23740138c5af1ab33058e3a | 253c7ffeddf24306a0510286c905a83d | 0df07cefe8574b428b998058a6c8e24b | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.341 | 02c79524aabe4f67a3f0b9e2dfc98426 | d4c55c75a03d4c4aa5548c739a0fc1c1 | 5681ef3b6b7542f295fffb65b114693c | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.345 | 926cec89f53b480388fe87f49f79b813 | b188bf19996e4300898f12cdc06c49dd | 9bd98cf850bd4f3daf6d3eb394aad92d | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.354 | 80d2f1d6cd7e46c98dad81b113050afb | ae00f600b34c4bd5a1aad6db2ad6b939 | ad24a21f231f4f1489b576aa0a561504 | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.355 | abdcc7433f624efcafd72a30478fb322 | 110c133582754763983b73ecbff01ff5 | 780e5781ee4649658e64552c993c327c | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.382 | fcc3821d12f942b6b33bb076ff1261a3 | da00c06f2769485d871c28ab604711fe | 2b9eb989432c49a08ed11653776a0f4f | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:09:06.383 | 30cc18b2af334ef08dd0e9e42d58cdb6 | ff301c892b90449c9ad42d80ce341bc5 | 95a641d4e29844de9a5d02fa44584d5a | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 21:54:38.854 | 7edc8b4a28f6452a8703e341a070d094 | 07efe432e63a40bea0a6cd21cd97fbd1 | be9759590e48460eaa4973827fdb329e | 98878-57c13e | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-27 22:05:12.088 | | | | 98878-57c13e | WARNING | config.get_scheduler:on_lock_lost:273 - ⚠️ Worker 98878-57c13e 失去 Application 锁
|
||||
2026-09-27 22:05:12.164 | | | | 98878-57c13e | ERROR | sqlalchemy.pool.base:_close_connection:376 - Exception closing connection <AdaptedConnection <asyncmy.connection.Connection object at 0x133919720>>
|
||||
Traceback (most recent call last):
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/pool/base.py", line 374, in _close_connection
|
||||
self._dialect.do_close(connection)
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 721, in do_close
|
||||
dbapi_connection.close()
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/asyncmy.py", line 114, in close
|
||||
self.await_(self._connection.ensure_closed())
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
|
||||
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
|
||||
value = await result
|
||||
File "asyncmy/connection.pyx", line 367, in ensure_closed
|
||||
File "/Users/jiaolongyan/miniconda3/envs/ShiErFeng/lib/python3.10/asyncio/streams.py", line 343, in wait_closed
|
||||
await self._protocol._get_close_waiter(self)
|
||||
BrokenPipeError: [Errno 32] Broken pipe
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
2026-09-28 09:00:15.337 | ba36c1c61a7244b9b65fb20c6a958f4b | bfb05dda36d44ff6ba420f064a88562b | 0cd6a97296f94e8eb3cc277ef7820515 | 62572-6b1f2c | WARNING | module_admin.service.login_service:__check_login_captcha:167 - 验证码已失效
|
||||
2026-09-28 09:00:15.339 | ba36c1c61a7244b9b65fb20c6a958f4b | bfb05dda36d44ff6ba420f064a88562b | 0cd6a97296f94e8eb3cc277ef7820515 | 62572-6b1f2c | WARNING | common.annotation.log_annotation:wrapper:197 - 验证码已失效
|
||||
2026-09-28 09:43:50.310 | d2b267db4ba8496a9df6021d1ba00300 | 6792967bd70f49aa9d408c6559c322c2 | af1fe648a9cb4179bee7c754859c46a9 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:03:42.880 | bdbd9aaa0d48441f9da094ea63c40e10 | 928c68eb45b94716b75a8548306e002b | e5eb4344cb14454a923416ac78753615 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:219 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:09:14.545 | 09e6fff8a6614865af978bfc63633250 | e6b47ff360ed4cdd98110a7a6d5eb8d3 | c949b290f3294eef8ab226d00d1d8dc1 | 62572-6b1f2c | WARNING | module_admin.service.login_service:__check_login_captcha:167 - 验证码已失效
|
||||
2026-09-28 10:09:14.546 | 09e6fff8a6614865af978bfc63633250 | e6b47ff360ed4cdd98110a7a6d5eb8d3 | c949b290f3294eef8ab226d00d1d8dc1 | 62572-6b1f2c | WARNING | common.annotation.log_annotation:wrapper:197 - 验证码已失效
|
||||
2026-09-28 10:21:53.236 | bf6b4c6c058b430ba3ccb1708e07ee8a | a619e28862d7481cb4ee6a216802a05e | 52f7de72fe134ee0875aed8b38e73d0f | 62572-6b1f2c | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘库存不存在或不在客户现场
|
||||
2026-09-28 10:24:08.862 | 4edf2b18b29f4c03a634d6b484e4fd67 | 2569f1304b6844d2a85cafac1c8918cf | aa800f8f383741f3b60f094c769104c8 | 62572-6b1f2c | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘库存不存在或不在客户现场
|
||||
2026-09-28 10:38:31.064 | 53e207894a1a41f387538142ea33bc05 | 42cd65a6daff43a8a9147a4999b26d31 | c0ac80f51bea4fa6aa8df8a835f37fd1 | 62572-6b1f2c | WARNING | module_admin.service.login_service:authenticate_user:133 - 密码错误
|
||||
2026-09-28 10:38:31.065 | 53e207894a1a41f387538142ea33bc05 | 42cd65a6daff43a8a9147a4999b26d31 | c0ac80f51bea4fa6aa8df8a835f37fd1 | 62572-6b1f2c | WARNING | common.annotation.log_annotation:wrapper:197 - 密码错误
|
||||
2026-09-28 10:38:41.223 | 56fee336ebe64917993bfd7622cf200a | 6a6c67a9eb114ff389c2bd0702fd4c52 | 5a6c9a772ba24ec78b40e1a6e0bfbc10 | 62572-6b1f2c | WARNING | module_admin.service.login_service:__check_login_captcha:170 - 验证码错误
|
||||
2026-09-28 10:38:41.223 | 56fee336ebe64917993bfd7622cf200a | 6a6c67a9eb114ff389c2bd0702fd4c52 | 5a6c9a772ba24ec78b40e1a6e0bfbc10 | 62572-6b1f2c | WARNING | common.annotation.log_annotation:wrapper:197 - 验证码错误
|
||||
2026-09-28 10:39:08.437 | d8a40c7b112d4f2f9aaa1576fbe53fa8 | ed3211272ba242328f859f7c91d3e344 | 10d9499662d74069b50e25aaf8b37797 | 62572-6b1f2c | ERROR | common.annotation.log_annotation:wrapper:202 - 托盘不符合首次入库条件
|
||||
2026-09-28 10:46:03.508 | 5991967288fa43dfa5a6271bc1558787 | 519a68df38094734ad0ea2d5eb570bf6 | aabf21cb05f94f91923652108f9b4d6a | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.514 | 7b8b0647325446d3927a0eb3c1dcd243 | 55f399b0c1db44d393c8cd2d317758d5 | 2232c53932ae4c37bb4b74e365106987 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.517 | 03591e4d5132466bb2669406f52cef54 | d84d01d8e8dd402cb0538289efa17894 | 6d298dccae1b4b919a1cf468528ee47a | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.519 | 18c5f5c4b502457082d2fb5d6e6effbc | b44f7d08c28b4d4e81f95fdc60ddeb37 | 90f4596111c1480bb8e947f6b01f9435 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.521 | 432adc6dbc374fceadbe7600b872bd53 | 78a958eb833f48588cf89f33d4de5a1b | 10445daddd45421b88d5229944c31445 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.524 | e993ea887654458f81e9843ec9d07952 | 9e6d6c1343994bff8e3dece2d20bf0ee | 20d503a7eb7a4f35acd582591e1b9d19 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.572 | 8b09a80bfeaa4215b959e02563810dd0 | ab4294c904a249ad863972aeb01a4d29 | 61794f5ad6124b2a94bd07a94fe237d3 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.579 | d17003e74b47423aa5abb8e2d0906871 | 13d207f3938441ec9de043ea99371274 | cd490dd2c5664d7388aae81e21445b7d | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.580 | 9242f746036b49a9949d71718d1de355 | 756c01642fac45308a0ccaa11632b4c1 | fe9b872a3d4343ac94b258fcec5181dd | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.580 | cc55b76dad1741a4833ff540b6dc442b | 1868b345057d4034baece403d93e9e29 | 1635a88ad5504efd94453a112f8e29de | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:03.581 | 0a91ce3f831e49efab0965ca18b5dbc2 | 1c8c68b5fbbe425fa500be69d556cd2f | 05210e3976ac44c6b3da75b8fea98952 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.232 | f487e375148c457e8461f85f6149a369 | 3a5a5f02edec4bfba6944d250ae99ab7 | 5d22ece20d3f42ee90834292972f30f7 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.274 | 66a66a5342114b6081ec37161581f57e | dad420640f514875b674f4599fba05dd | 6acb0ea3c8104c7898fa3c099bf25d14 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.274 | 9000e1326b404b78abb9e7aeb30391f0 | 92d042571c854790a8487bd599daa1a9 | c00b72e95d67432591a22ebd900b7afb | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.283 | ae676d2179cf424889f77806e3ee4c9b | 30c80ed1d960492993480baae5d9040c | 62f8d2bf510947b28672756f70787e76 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.284 | d46d37aa27024b2685db5581253a029c | a5fa9a3cc50441d88c544aeaf96c1163 | 6d24c8ac5f304b6e9db1a02569055f1c | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.286 | b13930203e3745f29dc8b957ce7f302f | 3d381f1978384f02a67a251ad9ccb556 | 1b39dc0017a7453cafc9ef0a9c9efb95 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.334 | 6c4f35c7f97f4e74ade5f9ade9b1ac1c | 8242777acb314c53bb718c935df83932 | c8b6b445700549deb14838304ea24a00 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.341 | 1e374f012ecd4cf89a7dc79b0b72f559 | 116770f6222349bbbff40109d5f04a3f | 1aa2b9c5a6cb4222968c76fefdf4ad56 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.343 | 2da707d80ed2471fa487726b3c9f7af0 | 99cb8278c4314f86aeb53ca3714df758 | e7f48812d7614e22ae8f5848c438ab92 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.343 | 0d6378e8376a40c690cc04b8bff3866d | b06087c979a04726984cd8c879649539 | 783d035f0e2b4de1801743d5e3c76a7f | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 10:46:16.344 | 19d2731983c44b71a04efd514a579320 | 867e7a717cd64541904c16f49b8f05f7 | 8e625c800e7c4f27a12cea3ec6ac15d2 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.155 | cce423f37fb84cd29e0d6dc6303d9c03 | 56f5f2575b854dada7b81b12b15c2813 | 98511d06168940dab87f39e4f3e3b7b0 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.157 | 2bd0170f8bcf4374ab60c0db43a32447 | c508a0855b214114861a1c4384124487 | 32ff02da68ef442286cefa9f84185a0d | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.158 | 8d2a13462f724be39c4da1a6048c4ae8 | 53c0230422de445982668b36765187e6 | 2b827f6120e644048802922125ba2ff3 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.158 | 4d757a1a06ad4ef88f8c8ebcc01ca69a | 3c2ce29cca144acb9f82b08899c1805e | 1b25762b8394413e80db760cf923c96e | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.186 | 8715cea358f2403fa725cdda75bf253e | 9d89c144d998477591fead7a9df7331b | a153e664a0ec4426ae86609093828d5b | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.186 | 0f2e087daaa34c4289178ab647ff55d0 | 59fdd73713ae44a58a2548e1ff1e8ab6 | 99dd9cd9abdc436288405f6448659ebf | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.188 | 530a2a1dd70a48088eff6ffa02f98a39 | 393cfa5715a84f0cb6852d4ab07cdcae | 12708e07f86f4ee98cda8526b68ebd8c | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.188 | 082b2cf3465f49d4b39a09866c97ee8e | cb8a906c394743f1ace0e260422059cb | e21eeb4d4ac3461899fcb5efea9fd432 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 12:03:10.188 | 44df9a204ccf475483a55fb0d5c327c8 | a3a29eac131e4c359fe19f78437e998d | 140d7a57b6574cee812154218f1b5407 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:09:48.985 | 13f4cbcd83154d8ba991194959fa526a | 0a23c5e16ed44f3192cb93434bd2975f | 613ac50ed4a04b7fa78e234c98c84116 | 62572-6b1f2c | WARNING | module_admin.service.login_service:__check_login_captcha:170 - 验证码错误
|
||||
2026-09-28 13:09:48.990 | 13f4cbcd83154d8ba991194959fa526a | 0a23c5e16ed44f3192cb93434bd2975f | 613ac50ed4a04b7fa78e234c98c84116 | 62572-6b1f2c | WARNING | common.annotation.log_annotation:wrapper:197 - 验证码错误
|
||||
2026-09-28 13:53:49.708 | 73a6d08358304a178e9ac9089881e2b9 | 18c4db78e68641d6a35cae565589b239 | ae356b29372d46e9b6e5570691549c67 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.312 | 5ab6fb1312b444fcb388106469ce9873 | 7628b918777b4c0982903a773e18bc7e | 4c7e41ef83204be2874d60a71a5a8eca | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.334 | faf5e90044ef4668ac186cf84dabb6b9 | 3a100294e01f45058eabf594ddd5eca0 | f0b1df93eb224f92975da239f92930b1 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.354 | ae5e3f9d480946dd8d03a7738b32d440 | d1a965ca11a74dc688f7b79c2b57954b | abc10506edc24506921e3b6865b22a0a | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.369 | cabfdd3271cd49c1bfa4b2b5a31f27ec | 7f35b31a5b9740d1b060e233682494b6 | bcc3d7c9e2d741c9a3b75353b46c4e82 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.374 | c80a006255e54dd9afd2be79e243b544 | fa8750a6a7a64d8f93b3c16778d651b8 | c23d3d73eb1c42d989a052ab32b4495d | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.378 | c3bcf46a67d3408cac2fd35495c10764 | 56998f6264ce4700bca3c8e61538b19f | 8708aa7c8273479e8ac33fc1f55829a5 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.382 | 33fd5d51ec9649fe9956fc6fc50966a0 | c2886cea734245f48618354d4fa22a47 | 6bcc309f42184588865183daf6607a82 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.469 | 90ab69c370fa4dc882923c30f9e6a6c2 | 4c0d88ad77cd42dba9cc3fc5e8da49c1 | 153bc5411f9148aeb2b368f558e7d006 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 13:59:45.469 | 3354a5201c6e475c815b3e93774c60ab | cf62d6123fde46b7b3eaf36fa7750263 | 425febf1654348d288e38e22a35f6d20 | 62572-6b1f2c | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 15:44:48.546 | f3d841a80e0c49b6a2ef12e0fb03c0e9 | d09057ad24a24e7ea4a22f44065f962d | 40ab25364cde46b29603eec07bb0410f | 19536-893d88 | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 18:15:15.372 | 55fe7aed5a5049eabdae0edd5e13b9c6 | e70f5cad542640e5b84a4a8f7bcbf349 | c96b83685c474b3fa2eb47238c10b3c3 | 19536-893d88 | WARNING | module_admin.service.login_service:get_current_user:277 - 用户token已失效,请重新登录
|
||||
2026-09-28 18:24:37.733 | | | | 19536-893d88 | WARNING | config.get_scheduler:on_lock_lost:273 - ⚠️ Worker 19536-893d88 失去 Application 锁
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,10 +14,10 @@ class ContextCleanupMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
在每个请求处理完成后清理上下文信息
|
||||
"""
|
||||
response = await call_next(request)
|
||||
# 请求处理完成后清理所有上下文变量
|
||||
try:
|
||||
return await call_next(request)
|
||||
finally:
|
||||
RequestContext.clear_all()
|
||||
return response
|
||||
|
||||
|
||||
def add_context_cleanup_middleware(app: FastAPI) -> None:
|
||||
|
||||
@@ -60,6 +60,22 @@ class OperationLogDao:
|
||||
|
||||
return operation_log_list
|
||||
|
||||
@classmethod
|
||||
async def get_scan_exception_log_list(
|
||||
cls, db: AsyncSession, oper_name: str | None, page_size: int
|
||||
) -> PageModel:
|
||||
"""按扫码日志标题和精确操作人查询首页异常记录。"""
|
||||
query = (
|
||||
select(SysOperLog)
|
||||
.where(
|
||||
SysOperLog.title == '扫码操作',
|
||||
SysOperLog.status == 1,
|
||||
SysOperLog.oper_name == oper_name if oper_name is not None else True,
|
||||
)
|
||||
.order_by(desc(SysOperLog.oper_time))
|
||||
)
|
||||
return await PageUtil.paginate(db, query, page_num=1, page_size=page_size, is_page=True)
|
||||
|
||||
@classmethod
|
||||
async def add_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel) -> SysOperLog:
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, Column, DateTime, Integer, LargeBinary, String
|
||||
from sqlalchemy import CHAR, Column, DateTime, Integer, String
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
from config.database import Base
|
||||
@@ -20,7 +20,7 @@ class SysNotice(Base):
|
||||
notice_title = Column(String(50), nullable=False, comment='公告标题')
|
||||
notice_type = Column(CHAR(1), nullable=False, comment='公告类型(1通知 2公告)')
|
||||
notice_content = Column(
|
||||
mysql.LONGBLOB if DataBaseConfig.db_type == 'mysql' else LargeBinary,
|
||||
mysql.LONGBLOB,
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False),
|
||||
comment='公告内容',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user