# 全要素采集管理系统_后端 **Repository Path**: geo_technology/task_management_system_back_end ## Basic Information - **Project Name**: 全要素采集管理系统_后端 - **Description**: 全要素采集管理系统_后端 - **Primary Language**: TypeScript - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-07 - **Last Updated**: 2026-09-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 天基数据工厂 - 遥感影像全要素采集管理系统 — 后端服务 对应《接口定义文档 V1.2》《MongoDB 表数据结构 V1.2》的后端实现。 - 技术栈:Node.js (≥18) / Koa.js / MongoDB (≥5.0) / Mongoose ODM - 定位:任务批次与图幅流转管理、两级质检闭环、每日工作量提报、统计看台、成果入库、审计日志 - 边界:**不提供任何矢量绘制能力**,OpenLayers 数据由 `/api/map` 接口只读提供 ## 目录结构 ``` task_management_system_back_end/ ├── package.json ├── .env.example # 环境变量样例(复制为 .env 使用) ├── scripts/ │ ├── check.mjs # 全量语法检查(npm run check) │ └── smoke.mjs # 纯函数冒烟测试(无需 MongoDB) └── src/ ├── server.js # 启动入口 ├── app.js # Koa 应用装配 ├── config/ # 环境配置 ├── db/ # MongoDB 连接与索引同步 ├── models/ # 12 个集合的 Mongoose 模型 ├── middlewares/ # 错误处理 / JWT / RBAC / 限流 / 参数校验 ├── services/ # 业务逻辑(领取、质检、密文日报、统计、看台…) ├── controllers/ # 控制器(13 个模块) ├── routes/ # 路由(13 个模块,挂载于 /api) ├── utils/ # 工具(JWT、密码、密文、GeoJSON、CSV、分页…) └── seeds/initAdmin.js # 初始化管理员(npm run seed:admin) ``` ## 快速开始 ```bash # 1. 安装依赖(本仓库使用本地 npm 缓存目录,避免用户目录权限问题) npm install --cache ./.npm-cache # 2. 配置环境变量 copy .env.example .env # Windows # 编辑 .env:MONGO_URI / JWT_SECRET / REPORT_CIPHER_KEY 必改 # 3. 启动 MongoDB 并初始化管理员 npm run seed:admin # 4. 启动服务(开发热重载 / 生产) npm run dev # nodemon npm start # node # 5. 语法检查 / 纯函数冒烟测试 npm run check node scripts/smoke.mjs # 6. 端到端测试(需 MongoDB;登录限流按 IP 15 分钟 10 次, # 重复运行前先重启服务清空内存限流桶) $env:MONGO_URI='mongodb://127.0.0.1:27017/vectorization_management_test' # PowerShell $env:PORT='3001' node src/server.js # 另开终端启动 node scripts/e2e.mjs # 自动重置测试库并跑全流程 30 项断言 ``` 默认管理员:`admin / Admin@123456`(通过 `ADMIN_USERNAME / ADMIN_PASSWORD` 覆盖,**生产必须修改**)。 ## 环境变量 | 变量 | 默认 | 说明 | |---|---|---| | `PORT` | 3000 | 服务端口 | | `MONGO_URI` | `mongodb://127.0.0.1:27017/vectorization_management` | 连接串 | | `JWT_SECRET` / `JWT_EXPIRES_IN` | — / `24h` | JWT 密钥与有效期 | | `BCRYPT_ROUNDS` | 10 | 密码哈希轮数 | | `UPLOAD_DIR` | `./uploads` | 附件存储目录 | | `GEOJSON_MAX_MB` / `ATTACHMENT_MAX_MB` | 50 / 20 | 上传大小限制 | | `REPORT_CIPHER_KEY` / `REPORT_CIPHER_TTL_HOURS` | — / 24 | 日报密文密钥与有效期 | | `TILE_GRID_DIVISION` | 8 | auto 模式切片网格 N×N(默认 8×8=64) | | `SYNC_INDEXES` | true | 启动时同步索引 | | `AUTO_ARCHIVE` | false | QCII 通过后是否自动入库 | | `DASHBOARD_SNAPSHOT` | false | 是否写看台快照 | | `RATE_LIMIT_WINDOW_MS` / `RATE_LIMIT_MAX` | 60000 / 600 | 全局兜底限流 | ## 接口速览(全部挂载于 `/api`) | 模块 | 路由 | 说明 | |---|---|---| | 认证 | `POST /auth/login`、`POST /auth/logout`、`GET /auth/me`、`POST /auth/change-password` | JWT;多角色身份头 `X-Active-Role` | | 用户 | `GET/POST /users`、`PATCH /users/:id`、`PATCH /users/:id/roles`、`GET /users/:id` | 仅 admin | | 分组 | `GET/POST /groups`、`PATCH /groups/:id`、`PATCH /groups/:id/members`、`GET /groups/:id` | 仅 admin,成员与 user_group_roles 双写 | | 任务 | `POST /tasks/upload`(GeoJSON 导入)、`GET /tasks`、`GET /tasks/:id`、`PATCH /tasks/:id/group`、`POST /tasks/:id/tile-import`、`GET /tasks/:id/sheets` | 导入自动拆分图幅;auto 模式生成切片 | | 图幅 | `GET /sheets`、`GET /sheets/my`、`POST /sheets/random-claim`、`PATCH /sheets/:id/progress`、`POST /sheets/:id/submit-qci`、`GET /sheets/:id`、`GET /sheets/:id/history` | 随机领取使用 findOneAndUpdate 原子抢占 | | 地图 | `GET /map/batch-layers`、`GET /map/sheet-layers`、`GET /map/sheet/:id` | OpenLayers 只读数据源 | | 质检 | `GET /reviews/queue`、`POST /reviews/:sheetId/pass`、`POST /reviews/:sheetId/reject`、`GET /reviews`、`GET /reviews/:sheetId/history` | QCI/QCII 按分组隔离;驳回必填原因 | | 日报 | `POST /daily-reports/decrypt-preview`、`POST /daily-reports`、`GET /daily-reports`、`PATCH /daily-reports/:id`、`POST /daily-reports/:id/submit`、`POST /daily-reports/:id/confirm`、`POST /daily-reports/copy-yesterday`、`POST /daily-reports/batch` | 密文服务端解密校验;唯一约束防重复 | | 统计 | `GET /statistics/me/day`、`/me/month`、`/groups`、`/members`、`/qc`、`/batch`、`/export` | 按角色分权 | | 看台 | `GET /dashboard/overview`、`/groups`、`/members-daily`、`/trend` | 仅 admin | | 入库 | `POST /archive/:sheetId`、`GET /archive`、`GET /archive/export`、`GET /archive/geojson-export` | 前置状态 qcii_passed | | 审计 | `GET /audit-logs`、`GET /audit-logs/export` | 仅 admin | | 文件 | `POST /files/upload`、`GET /files/:id` | 附件/成果上传下载 | 响应信封:`{ "code": 0, "message": "ok", "data": ... }`,错误码见《接口定义文档 V1.2》1.3 节。 ## 关键设计 ### 1. 图幅八状态机 `pending_claim → in_progress → pending_qci → qci_passed → qcii_passed → archived` (`qci_passed`=待二检,`qcii_passed`=待入库;`qci_rejected` / `qcii_rejected` 返回 DP 修改后重新走完整链路)。 所有状态流转均以「前置状态」作为 `findOneAndUpdate` 过滤条件,并发下不会互相覆盖,冲突返回 409。 ### 2. 随机领取(并发安全) ``` Sheet.findOneAndUpdate({ _id, status: "pending_claim", groupId: {$in: 本人分组} }, { $set: { status: "in_progress", assigneeId: 当前用户, claimedAt: now } }) ``` 候选采样 `$sample` 后逐个原子抢占,抢不到的自动跳过补足,保证同一图幅不会被两人同时领取。 ### 3. 日报密文(参考协议) 密文格式:`base64url(payloadJson) + "." + hex(hmac_sha256(payloadJson, key))` payload 字段:`v / userId / date / generatedAt / nonce / sheets[{sheetId, sheetNo, tile512Count, categoryCounts}]`。 后端校验:HMAC 签名(防篡改)→ 有效期(默认 24h)→ 归属用户一致 → 明细完整性;落库时写入 `encryptedPayloadHash`,配合 `{date, userId, sheetId}` 唯一索引拦截重复提交。真实 ArcGIS Toolbox 需与后端共享同一密钥并遵循该格式(待确认 10.3)。 ### 4. 数据范围隔离 - admin:全局; - DP:本人领取图幅(`/sheets/my`)与本人日报; - QCI / QCII:本人担任该级质检的分组(依据 `user_group_roles`),地图/统计同口径。 多角色用户通过请求头 `X-Active-Role: DP|QCI|QCII` 声明本次操作身份,写入审计日志的 `roleUsed`。 ### 5. 审计 所有写操作(领取、进度、提交、质检通过/驳回、日报、入库、人员/分组变更)由 `services/audit.service.js` 统一写入 `audit_logs`;图幅流转历史接口由审计日志聚合生成。 ## 已知限制与待确认 - 限流为单进程内存实现,多实例部署需替换为 Redis。 - 日报统计、看台为实时聚合(JS 内存聚合),数据量大后建议改用聚合管道 + `dashboard_snapshots` 预聚合(`DASHBOARD_SNAPSHOT=true`)。 - 与《需求规格说明书 V1.2》第 10 节对应的待确认项(图幅号字段、切片来源、加密密钥管理、日报审核人、二检返工链路、成果存放、跨组协作)均在代码中以常量/配置形式给出默认值,见两文档附录。