# UrbanOps 开放 API 对接接口文档 > **版本**: v1.1 > **更新日期**: 2026-06-25 > **适用对象**: 第三方系统对接开发人员 / Co-Claw AI 助手 > **鉴权方式**: HMAC-SHA256 API 签名 --- ## 目录 1. [概述](#1-概述) 2. [签名鉴权机制](#2-签名鉴权机制) 3. [通用规范](#3-通用规范) 4. [接口 1:团队考核查询(Co-Claw AI 专用)](#4-接口-1团队考核查询Co-Claw-ai-专用) 5. [接口 2:人员查询](#5-接口-2人员查询) 6. [接口 3:班组查询](#6-接口-3班组查询) 7. [接口 4:考勤查询](#7-接口-4考勤查询) 8. [接口 5:工单查询](#8-接口-5工单查询) 9. [接口 6:物料查询](#9-接口-6物料查询) 10. [接口 7:全量工单统计](#10-接口-7全量工单统计) 11. [错误码说明](#11-错误码说明) 12. [附录:客户端签名代码示例](#12-附录客户端签名代码示例) --- ## 1. 概述 ### 1.1 基本信息 | 项目 | 说明 | |------|------| | 接口协议 | HTTPS | | 接口地址 | `https://{host}/open-api/` | | 鉴权方式 | HMAC-SHA256 API 签名(详见第2节) | | 响应格式 | JSON | | 字符编码 | UTF-8 | ### 1.2 接口列表 | # | 接口名称 | 路径 | 方法 | 说明 | |---|---------|------|------|------| | 1 | **团队考核查询** ⭐ | `/open-api/team-assessment/query` | GET | **Co-Claw AI 专用**:班组/人员考核聚合接口,含考勤/工单/耗材三维评分 | | 2 | 人员查询 | `/open-api/staff/query` | GET | 获取人员档案,支持按部门筛选 | | 3 | 班组查询 | `/open-api/teams/query` | GET | 获取班组信息,含班组长和人员数量 | | 4 | 考勤查询 | `/open-api/attendance/query` | GET | 获取考勤打卡数据 | | 5 | 工单查询 | `/open-api/tasks/query` | GET | 获取工单全生命周期数据 | | 6 | 物料查询 | `/open-api/materials/query` | GET | 获取物料消耗与照片数据 | | 7 | 全量工单统计 | `/open-api/full-order-statistics/query` | GET | 获取公司/班组工单/巡查/养护完成数量 | ### 1.3 接入前提 接入前需向 UrbanOps 平台申请以下凭证(由运维人员配置到 Redis): | 参数 | 说明 | |------|------| | `appId` | 应用唯一标识(由平台分配) | | `appSecret` | 应用密钥(妥善保管,不可泄露) | --- ## 2. 签名鉴权机制 ### 2.1 签名参数 每个请求必须携带以下 **4 个请求头**: | 请求头 | 类型 | 必填 | 说明 | |--------|------|------|------| | `appId` | String | ✅ | 平台分配的应用 ID | | `timestamp` | String | ✅ | 当前 Unix 时间戳(**毫秒**),与服务器时间误差不能超过 60 秒 | | `nonce` | String | ✅ | 随机字符串,**长度 ≥ 10**,每次请求必须不同(防重放) | | `sign` | String | ✅ | 签名字符串(SHA256 计算结果,**小写十六进制**) | ### 2.2 签名算法 #### 签名字符串构造规则 ``` 签名字符串 = queryParams(按key字母序排列的value拼接) + headerParams(按key字母序排列的value拼接) + appSecret ``` **具体步骤:** 1. **排序 Query 参数值**:将 URL 中所有 query 参数按 key 字母升序排列,拼接所有 value 2. **拼接 Header 参数值**:按 key 字母升序(即 `appId` → `nonce` → `timestamp`),依次拼接它们的值 3. **追加 appSecret**:在最后拼接 `appSecret` 4. **计算 SHA256**:对上述完整字符串计算 SHA256 哈希,转为**小写十六进制**字符串 #### 签名公式 ``` sign = SHA256( sortedQueryParamValues + appId值 + nonce值 + timestamp值 + appSecret ) ``` > **注意**:GET 请求无 body,body 部分为空字符串(不参与签名)。 ### 2.3 签名示例 假设: - `appId` = `my_app_001` - `appSecret` = `abc123xyz` - 请求 URL:`/open-api/staff/query?deptId=100` **步骤拆解:** ``` queryParams(sorted by key): deptId=100 → values = "100" headerParams(sorted by key): appId=my_app_001, nonce=a1b2c3d4e5f6, timestamp=1749696000000 → values = "my_app_001" + "a1b2c3d4e5f6" + "1749696000000" signStr = "100" + "my_app_001" + "a1b2c3d4e5f6" + "1749696000000" + "abc123xyz" sign = SHA256(signStr) ``` ### 2.4 签名校验规则 | 校验项 | 规则 | 失败返回 | |--------|------|----------| | appId 非空 | 必须传递 | `400 签名不正确` | | timestamp 时效 | `|serverTime - timestamp| ≤ 60s` | `400 签名不正确` | | nonce 长度 | `len(nonce) ≥ 10` | `400 签名不正确` | | sign 非空 | 必须传递 | `400 签名不正确` | | 签名比对 | `serverSign == clientSign` | `400 签名不正确` | | nonce 防重放 | 同一 appId + nonce 组合只能使用一次 | `900 存在重复请求` | --- ## 3. 通用规范 ### 3.1 通用响应格式 ```json { "code": 0, "msg": "成功", "data": [ ... ] } ``` | 字段 | 类型 | 说明 | |------|------|------| | `code` | Integer | `0` 表示成功,非 `0` 表示失败 | | `msg` | String | 提示信息 | | `data` | Array | 业务数据列表(根据查询条件返回所有匹配记录) | ### 3.2 日期时间格式 所有日期时间字段统一使用格式:`yyyy-MM-dd HH:mm:ss` 示例:`2026-06-11 09:00:00` --- ## 4. 接口 1:团队考核查询(Co-Claw AI 专用)⭐ > **设计定位**:本接口专为 Co-Claw AI 助手设计,提供班组/人员考核数据的**一站式聚合查询**。 > 接口内部完成考勤、工单、耗材三维度评分计算,Co-Claw 可直接使用返回的得分和扣分明细进行考核分析, > 无需自行计算。默认只返回汇总指标(`includeDetail=false`),Token 友好。 ### 4.1 考核背景 本接口基于《房山区园林所养护队智慧化管理制度》设计,考核满分 100 分,三大维度: | 维度 | 满分 | 主要扣分规则 | |------|------|-------------| | 工单上线率与及时率 | **40分** | 超24h补录 -1分/次 · 超时>2h -1分/次 | | 人员考勤管理 | **30分** | 缺勤/未打卡 -1分/次 · 缺共同处理人 -1分/次 · 未佩戴工卡 -1分/次 · 班组长记录仪未开 -1分/次 | | 人机材录入 | **30分** | 工单完成24h内未上报耗材 -2分/次 | ### 4.2 基本信息 | 项目 | 说明 | |------|------| | 路径 | `/open-api/team-assessment/query` | | 方法 | `GET` | | 说明 | 团队考核聚合查询。支持 **team 模式**(班组→人员+工单→考勤+耗材,三层结构)和 **staff 模式**(人员→考勤+工单→耗材) | ### 4.3 请求参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `keyword` | String | **是** | — | 查询关键字:班组名称或人员姓名,**支持模糊匹配** | | `queryMode` | String | 否 | `team` | 查询模式:`team` 按班组查询 / `staff` 按人员查询 | | `startDate` | String | 否 | 当月1日 | 考核开始日期(格式:yyyy-MM-dd) | | `endDate` | String | 否 | 今天 | 考核结束日期(格式:yyyy-MM-dd) | | `includeDetail` | Boolean | 否 | `false` | 是否返回明细数据。`false` 仅返回汇总指标(推荐),`true` 同时返回考勤明细和工单耗材明细 | ### 4.4 响应字段(queryMode=team) #### 顶层结构 | 字段 | 类型 | 说明 | |------|------|------| | `queryMode` | String | 查询模式(`team` 或 `staff`) | | `period` | PeriodVO | 考核周期 | | `teams` | Array\ | 班组考核列表(team 模式) | | `staffList` | Array\ | 人员考核列表(staff 模式,与 teams 互斥) | #### TeamVO — 班组考核(第一层) | 字段 | 类型 | 说明 | |------|------|------| | `teamId` | Long | 班组ID | | `teamName` | String | 班组名称 | | `parentTeamName` | String | 上级部门名称(所属养护所) | | `leaderName` | String | 班组长姓名 | | `totalMembers` | Long | 班组人数 | | `score` | ScoreVO | 考核得分(详见下方) | | `orderStats` | OrderStatsVO | 工单统计 | | `attendanceStats` | AttendanceStatsVO | 考勤统计 | | `materialStats` | MaterialStatsVO | 耗材统计 | | `members` | Array\ | 人员列表(**第二层**,含个人考核指标) | | `orders` | Array\ | 工单列表(**第二层**,含耗材) | #### ScoreVO — 考核得分 | 字段 | 类型 | 说明 | |------|------|------| | `total` | Integer | 总分(满分100) | | `orderScore` | Integer | 工单得分(满分40) | | `attendanceScore` | Integer | 考勤得分(满分30) | | `materialScore` | Integer | 人机材得分(满分30) | | `deductions` | Array\ | 扣分明细列表 | **DeductionVO:** | 字段 | 类型 | 说明 | |------|------|------| | `rule` | String | 扣分规则描述 | | `count` | Integer | 触发次数 | | `points` | Integer | 扣分(负数) | #### OrderStatsVO — 工单统计 | 字段 | 类型 | 说明 | |------|------|------| | `totalOrders` | Long | 总工单数 | | `completedOrders` | Long | 已完成工单数 | | `completionRate` | BigDecimal | 完成率(%) | | `onlineRate` | BigDecimal | 线上化率(%) | | `responseWithin30Min` | Long | 30分钟内响应数 | | `responseRate30Min` | BigDecimal | 30分钟响应率(%) | | `overdueOrders` | Long | 超期工单数 | | `lateSupplementOrders` | Long | 超24小时补录工单数 | #### AttendanceStatsVO — 考勤统计 | 字段 | 类型 | 说明 | |------|------|------| | `totalCheckins` | Long | 总打卡次数 | | `absentCount` | Long | 缺勤人次 | | `missedCheckinCount` | Long | 未触发电子围栏打卡次数 | | `trajectoryLowCount` | Long | 轨迹覆盖率不达标次数 | | `missingCoHandlerCount` | Long | 缺共同处理人次数 | | `missingCardCount` | Long | 未佩戴工卡次数 | | `multiCardViolation` | Boolean | 是否存在一人多卡违规 | | `leaderRecorderOffCount` | Long | 班组长记录仪未开机次数 | #### MaterialStatsVO — 耗材统计 | 字段 | 类型 | 说明 | |------|------|------| | `ordersWithMaterial` | Long | 已上报耗材的工单数 | | `ordersMissingMaterial` | Long | 缺耗材上报的工单数 | | `totalMaterialItems` | Long | 耗材总用量 | | `materialTypes` | Long | 耗材种类数 | #### MemberVO — 人员信息(第二层) | 字段 | 类型 | 说明 | |------|------|------| | `userId` | Long | 人员ID | | `userName` | String | 姓名 | | `mobile` | String | 手机号 | | `postNames` | String | 岗位名称(逗号分隔) | | `cardNumber` | String | 工卡号 | | `isInner` | Integer | 是否内部人员(1=内部,2=外部) | | `personalScore` | PersonalScoreVO | 个人考核指标 | | `attendances` | Array\ | 考勤明细(**第三层**,`includeDetail=true` 时返回) | | `orders` | Array\ | 个人工单(**第三层**,`includeDetail=true` 时返回) | #### PersonalScoreVO — 个人考核指标 | 字段 | 类型 | 说明 | |------|------|------| | `attendanceDays` | Long | 出勤天数 | | `lateCount` | Long | 迟到次数 | | `absentCount` | Long | 缺勤次数 | | `assignedOrders` | Long | 承担工单数 | | `completedOrders` | Long | 完成工单数 | | `completionRate` | BigDecimal | 完成率(%) | | `overdueOrders` | Long | 超期工单数 | | `materialReported` | Long | 已上报耗材工单数 | | `materialMissing` | Long | 缺耗材上报工单数 | #### OrderVO — 工单信息(第二层/第三层) | 字段 | 类型 | 说明 | |------|------|------| | `orderNo` | String | 工单号 | | `orderName` | String | 工单名称 | | `orderType` | String | 工单类型:Q=快速,C=普通,O=其他 | | `workerName` | String | 接单人姓名 | | `status` | Integer | 审批状态 | | `buzStatus` | String | 业务状态(completed=已完成) | | `commitDate` | String | 发布时间 | | `finishDate` | String | 完成时间 | | `expectedFinishDate` | String | 期望完成时间 | | `responseMinutes` | Long | 响应时长(分钟) | | `isOverdue` | Boolean | 是否超期(`finishDate > expectedFinishDate`) | | `isLateSupplement` | Boolean | 是否超24小时补录 | | `hasMaterialReported` | Boolean | 是否已上报耗材 | | `coHandlers` | Array\ | 共同处理人姓名 | | `materials` | Array\ | 耗材明细(**第三层**,`includeDetail=true` 时返回) | #### 其他嵌套 VO **AttendanceRecordVO:** | 字段 | 类型 | 说明 | |------|------|------| | `punchType` | String | 打卡类型(上班/下班) | | `punchTime` | String | 打卡时间 | | `address` | String | 打卡地址 | **MaterialVO:** | 字段 | 类型 | 说明 | |------|------|------| | `materialName` | String | 耗材名称 | | `specifications` | String | 规格 | | `userCount` | Long | 使用数量 | | `unitName` | String | 单位 | | `classifyName` | String | 分类 | **PeriodVO:** | 字段 | 类型 | 说明 | |------|------|------| | `start` | String | 考核开始日期(yyyy-MM-dd) | | `end` | String | 考核结束日期(yyyy-MM-dd) | ### 4.5 请求示例 ```bash # team 模式:按班组名称查询考核数据(仅汇总) curl -X GET "https://test.jichengshanshui.com.cn:28302/open-api/team-assessment/query?keyword=养护一班&queryMode=team&startDate=2026-06-01&endDate=2026-06-30" \ -H "appId: my_app_001" \ -H "timestamp: 1749696000000" \ -H "nonce: c3d4e5f6g7h8i9j0" \ -H "sign: ..." # staff 模式:按人员姓名查询考核数据(含考勤明细和工单耗材) curl -X GET "https://test.jichengshanshui.com.cn:28302/open-api/team-assessment/query?keyword=张三&queryMode=staff&includeDetail=true" \ -H "appId: my_app_001" \ -H "timestamp: 1749696000000" \ -H "nonce: d4e5f6g7h8i9j0k1" \ -H "sign: ..." ``` ### 4.6 响应示例(team 模式,includeDetail=false) ```json { "code": 0, "msg": "成功", "data": { "queryMode": "team", "period": { "start": "2026-06-01", "end": "2026-06-30" }, "teams": [ { "teamId": 100, "teamName": "养护一班", "parentTeamName": "房山区园林所", "leaderName": "张三", "totalMembers": 15, "score": { "total": 85, "orderScore": 34, "attendanceScore": 25, "materialScore": 26, "deductions": [ { "rule": "工单超24小时补录", "count": 1, "points": -1 }, { "rule": "工单超时>2小时", "count": 2, "points": -2 }, { "rule": "缺勤/未打卡", "count": 5, "points": -5 }, { "rule": "人机材24h内未上报", "count": 1, "points": -2 } ] }, "orderStats": { "totalOrders": 45, "completedOrders": 42, "completionRate": 93.3, "onlineRate": 97.8, "responseWithin30Min": 40, "responseRate30Min": 88.9, "overdueOrders": 2, "lateSupplementOrders": 1 }, "attendanceStats": { "totalCheckins": 570, "absentCount": 5, "missedCheckinCount": 5, "trajectoryLowCount": 1, "missingCoHandlerCount": 0, "missingCardCount": 0, "multiCardViolation": false, "leaderRecorderOffCount": 0 }, "materialStats": { "ordersWithMaterial": 42, "ordersMissingMaterial": 1, "totalMaterialItems": 230, "materialTypes": 15 }, "members": [ { "userId": 1001, "userName": "张三", "mobile": "13800138001", "postNames": "班组长,技术员", "cardNumber": "DEV001", "isInner": 1, "personalScore": { "attendanceDays": 28, "lateCount": 0, "absentCount": 0, "assignedOrders": 8, "completedOrders": 8, "completionRate": 100.0, "overdueOrders": 0, "materialReported": 8, "materialMissing": 0 } } ], "orders": [ { "orderNo": "WO202606250001", "orderName": "树木修剪", "orderType": "C", "workerName": "张三", "status": 1, "buzStatus": "completed", "commitDate": "2026-06-25 09:00:00", "finishDate": "2026-06-25 16:30:00", "expectedFinishDate": "2026-06-25 18:00:00", "responseMinutes": 15, "isOverdue": false, "isLateSupplement": false, "hasMaterialReported": true, "coHandlers": ["李四"] } ] } ] } } ``` ### 4.7 响应示例(staff 模式,includeDetail=true) ```json { "code": 0, "msg": "成功", "data": { "queryMode": "staff", "period": { "start": "2026-06-01", "end": "2026-06-25" }, "staffList": [ { "userId": 1001, "userName": "张三", "mobile": "13800138001", "deptId": 100, "deptName": "养护一班", "postNames": "班组长", "cardNumber": "DEV001", "isInner": 1, "assessment": { "attendanceDays": 28, "lateCount": 0, "absentCount": 0, "assignedOrders": 8, "completedOrders": 8, "completionRate": 100.0, "overdueOrders": 0, "materialReported": 8, "materialMissing": 0 }, "attendances": [ { "punchType": "上班", "punchTime": "2026-06-25 08:00:00", "address": "北京市房山区xx路" }, { "punchType": "下班", "punchTime": "2026-06-25 17:30:00", "address": "北京市房山区xx路" } ], "orders": [ { "orderNo": "WO202606250001", "orderName": "树木修剪", "orderType": "C", "workerName": "张三", "status": 1, "buzStatus": "completed", "commitDate": "2026-06-25 09:00:00", "finishDate": "2026-06-25 16:30:00", "expectedFinishDate": "2026-06-25 18:00:00", "responseMinutes": 15, "isOverdue": false, "isLateSupplement": false, "hasMaterialReported": true, "coHandlers": ["李四"], "materials": [ { "materialName": "高效氯氰菊酯", "specifications": "500ml/瓶", "userCount": 2, "unitName": "瓶", "classifyName": "药品" } ] } ] } ] } } ``` ### 4.8 使用建议(Co-ClawCo-Claw) | 场景 | 推荐参数 | 说明 | |------|----------|------| | 月底考核排名 | `keyword=养护&queryMode=team` | 模糊查询所有养护班组,返回考核得分用于排名 | | 单班组详情分析 | `keyword=养护一班&queryMode=team&includeDetail=true` | 查看特定班组全部明细,分析扣分原因 | | 个人绩效查询 | `keyword=张三&queryMode=staff&includeDetail=true` | 查看某人出勤、工单、耗材完整记录 | | 日常快速巡检 | `keyword=养护&includeDetail=false` | 只看汇总指标,Token 最少 | --- ## 5. 接口 2:人员查询 ### 4.1 基本信息 | 项目 | 说明 | |------|------| | 路径 | `/open-api/staff/query` | | 方法 | `GET` | | 说明 | 获取人员基础档案信息,支持按部门、姓名、手机号等条件筛选 | ### 4.2 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `name` | String | 否 | 姓名,模糊匹配 | | `mobile` | String | 否 | 手机号,模糊匹配 | | `deptId` | Long | 否 | 部门ID,精确匹配 | | `status` | Integer | 否 | 状态:0=启用,1=禁用 | | `busiLine` | String | 否 | 业务线:yl=园林,wy=物业,sz=市政 | ### 4.3 响应字段 | 字段 | 类型 | 说明 | |------|------|------| | `id` | Long | 人员ID | | `username` | String | 用户名 | | `nickname` | String | 姓名 | | `mobile` | String | 手机号 | | `deptId` | Long | 部门ID | | `deptName` | String | 部门名称(如"养护一班") | | `postIds` | Array\ | 岗位ID列表 | | `postNames` | String | 岗位名称(逗号分隔,如"班组长,技术员") | | `busiLine` | String | 业务线 | | `status` | Integer | 状态(0=启用,1=禁用) | | `cardNumber` | String | 工卡号(设备编号,用于考勤打卡) | | `isInner` | Integer | 是否内部人员(1=内部,2=外部) | | `createTime` | String | 入职日期(格式:yyyy-MM-dd HH:mm:ss) | ### 4.4 请求示例 ```bash curl -X GET "https://test.jichengshanshui.com.cn:28302/open-api/staff/query?deptId=100" \ -H "appId: my_app_001" \ -H "timestamp: 1749696000000" \ -H "nonce: a1b2c3d4e5f6g7h8" \ -H "sign: 3a5f8c9e2b1d4f6a8c0e2b4d6f8a0c2e4b6d8f0a" ``` ### 4.5 响应示例 ```json { "code": 0, "msg": "成功", "data": [ { "id": 1001, "username": "zhangsan", "nickname": "张三", "mobile": "13800138001", "deptId": 100, "deptName": "养护一班", "postIds": [10, 20], "postNames": "班组长,技术员", "busiLine": "yl", "status": 0, "cardNumber": "DEV001", "isInner": 1, "createTime": "2024-01-15 00:00:00" } ], "total": 150 } ``` --- ## 6. 接口 3:班组查询 ### 5.1 基本信息 | 项目 | 说明 | |------|------| | 路径 | `/open-api/teams/query` | | 方法 | `GET` | | 说明 | 获取组织架构班组信息,含班组长、所属养护所、人员数量 | ### 5.2 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `name` | String | 否 | 班组名称,模糊匹配 | | `parentId` | Long | 否 | 父部门ID(上级组织),精确匹配 | | `status` | Integer | 否 | 状态:0=启用,1=禁用 | ### 5.3 响应字段 | 字段 | 类型 | 说明 | |------|------|------| | `id` | Long | 班组ID | | `name` | String | 班组名称 | | `parentId` | Long | 父部门ID | | `parentName` | String | 父部门名称(所属养护所) | | `leaderUserId` | Long | 班组长人员ID(Staff.id) | | `leaderUserName` | String | 班组长姓名 | | `memberCount` | Long | 人员数量 | | `sort` | Integer | 排序 | | `status` | Integer | 状态(0=启用,1=禁用) | ### 5.4 请求示例 ```bash curl -X GET "https://test.jichengshanshui.com.cn:28302/open-api/teams/query" \ -H "appId: my_app_001" \ -H "timestamp: 1749696000000" \ -H "nonce: b2c3d4e5f6g7h8i9" \ -H "sign: ..." ``` ### 5.5 响应示例 ```json { "code": 0, "msg": "成功", "data": [ { "id": 100, "name": "养护一班", "parentId": 1, "parentName": "蓟城山水集团", "leaderUserId": 1001, "leaderUserName": "张三", "memberCount": 15, "sort": 1, "status": 0 } ], "total": 12 } } ``` --- ## 7. 接口 4:考勤查询 ### 6.1 基本信息 | 项目 | 说明 | |------|------| | 路径 | `/open-api/attendance/query` | | 方法 | `GET` | | 说明 | 获取电子围栏打卡与位置数据,支持按班组、人员、时间范围筛选 | ### 6.2 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `userId` | Long | 否 | 人员ID,精确匹配 | | `deptId` | Long | 否 | 部门ID(班组),精确匹配 | | `busiLine` | String | 否 | 业务线 | | `punchType` | String | 否 | 打卡类型(上班/下班) | | `startTime` | String | 否 | 开始时间(格式:yyyy-MM-dd HH:mm:ss) | | `endTime` | String | 否 | 结束时间(格式:yyyy-MM-dd HH:mm:ss) | ### 6.3 响应字段 | 字段 | 类型 | 说明 | |------|------|------| | `id` | Integer | 记录ID | | `deviceCode` | String | 设备编码 | | `deviceName` | String | 设备名称 | | `punchType` | String | 打卡类型 | | `userId` | Long | 人员ID | | `nickname` | String | 人员姓名 | | `deptId` | Long | 部门ID | | `deptName` | String | 部门名称 | | `address` | String | 打卡地址 | | `latGcj02` | BigDecimal | 高德纬度(GCJ-02坐标系) | | `lngGcj02` | BigDecimal | 高德经度(GCJ-02坐标系) | | `lat` | BigDecimal | WGS84纬度 | | `lng` | BigDecimal | WGS84经度 | | `createTime` | String | 打卡时间 | > **注意**:当前版本暂不支持"轨迹点集""是否佩戴工卡""记录仪状态"字段(数据库无对应存储)。待业务系统确认后将补充。 ### 6.4 响应示例 ```json { "code": 0, "msg": "成功", "data": [ { "id": 1, "deviceCode": "DEV001", "deviceName": "打卡机01", "punchType": "上班", "userId": 1001, "nickname": "张三", "deptId": 100, "deptName": "养护一班", "address": "北京市西城区xx路xx号", "latGcj02": 39.9042, "lngGcj02": 116.4074, "lat": 39.9010, "lng": 116.4030, "createTime": "2026-06-11 08:30:00" } ], "total": 320 } } ``` --- ## 8. 接口 5:工单查询 ### 7.1 基本信息 | 项目 | 说明 | |------|------| | 路径 | `/open-api/tasks/query` | | 方法 | `GET` | | 说明 | 获取工单全生命周期数据,含发布时间、状态、接单渠道、共同处理人 | ### 7.2 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `orderNo` | String | 否 | 工单号,模糊匹配 | | `orderName` | String | 否 | 工单名称,模糊匹配 | | `busiLine` | String | 否 | 业务线 | | `status` | Integer | 否 | 审批状态 | | `buzStatus` | String | 否 | 业务状态 | | `workerId` | Long | 否 | 接单人ID | | `startDate` | String | 否 | 发布时间起始(yyyy-MM-dd HH:mm:ss) | | `endDate` | String | 否 | 发布时间截止(yyyy-MM-dd HH:mm:ss) | ### 7.3 响应字段 | 字段 | 类型 | 说明 | |------|------|------| | `id` | Long | 工单ID | | `orderNo` | String | 工单号 | | `orderName` | String | 工单名称 | | `orderType` | String | 工单类型:Q=快速,C=普通,O=其他 | | `busiLine` | String | 业务线 | | `commitDate` | String | 发布时间(工单派发时间) | | `finishDate` | String | 完成时间 | | `userId` | Long | 发布人ID | | `userName` | String | 发布人姓名 | | `workerId` | Long | 接单人ID | | `workerName` | String | 接单人姓名 | | `coHandlers` | Array\ | 共同处理人姓名列表 | | `woSourceName` | String | 接单渠道(小程序/人工派单等) | | `status` | Integer | 审批状态 | | `buzStatus` | String | 业务状态 | | `remark` | String | 备注 | | `photos` | Array\ | 工单附件照片 | **PhotoVO 结构:** | 字段 | 类型 | 说明 | |------|------|------| | `busiType` | String | 业务类型:`01`=问题照片,`02`=处理中,`03`=完成 | | `fileName` | String | 文件名 | | `url` | String | 文件URL | ### 7.4 响应示例 ```json { "code": 0, "msg": "成功", "data": [ { "id": 5001, "orderNo": "WO202606110001", "orderName": "树木修剪", "orderType": "C", "busiLine": "yl", "commitDate": "2026-06-11 09:00:00", "finishDate": null, "userId": 1, "userName": "管理员", "workerId": 1001, "workerName": "张三", "coHandlers": ["李四", "王五"], "woSourceName": "小程序", "status": 1, "buzStatus": "processing", "remark": "紧急处理", "photos": [ { "busiType": "01", "fileName": "problem.jpg", "url": "https://xxx.com/file/abc.jpg" } ] } ], "total": 85 } } ``` --- ## 9. 接口 6:物料查询 ### 8.1 基本信息 | 项目 | 说明 | |------|------| | 路径 | `/open-api/materials/query` | | 方法 | `GET` | | 说明 | 获取物料消耗记录与人机材照片数据,支持按工单筛选 | ### 8.2 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `orderNo` | String | 否 | 工单号,精确匹配 | | `classifyName` | String | 否 | 物料分类名,模糊匹配 | | `typeName` | String | 否 | 物料类型名,模糊匹配 | | `userId` | Long | 否 | 使用人ID | | `startDate` | String | 否 | 开始时间(yyyy-MM-dd HH:mm:ss) | | `endDate` | String | 否 | 结束时间(yyyy-MM-dd HH:mm:ss) | ### 8.3 响应字段 | 字段 | 类型 | 说明 | |------|------|------| | `id` | Long | 记录ID | | `orderNo` | String | 关联工单号 | | `materialName` | String | 物料名称 | | `specifications` | String | 规格 | | `userCount` | Long | 使用数量 | | `unitName` | String | 单位 | | `classifyName` | String | 物料分类 | | `typeName` | String | 物料类型 | | `typeDetailName` | String | 物料明细类型 | | `userId` | Long | 使用人ID | | `userName` | String | 使用人姓名 | | `source` | Integer | 来源:1=工单,2=巡检,3=养护 | | `createTime` | String | 上传时间 | | `photos` | Array\ | 人机材照片(结构同工单接口) | ### 8.4 响应示例 ```json { "code": 0, "msg": "成功", "data": [ { "id": 3001, "orderNo": "WO202606110001", "materialName": "高效氯氰菊酯", "specifications": "500ml/瓶", "userCount": 5, "unitName": "瓶", "classifyName": "药品", "typeName": "杀虫剂", "typeDetailName": "有机磷", "userId": 1001, "userName": "张三", "source": 1, "createTime": "2026-06-11 14:30:00", "photos": [ { "busiType": "02", "fileName": "material.jpg", "url": "https://xxx.com/file/def.jpg" } ] } ], "total": 45 } } ``` --- ## 10. 接口 7:全量工单统计 ### 9.1 基本信息 | 项目 | 说明 | |------|------| | 路径 | `/open-api/full-order-statistics/query` | | 方法 | `GET` | | 说明 | 获取各公司/班组在一段时间内实际完成的工单数量、巡查记录数量、养护记录数量 | ### 9.2 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `companyId` | Long | 否 | 公司/班组ID,精确匹配 | | `companyName` | String | 否 | 公司/班组名称,模糊匹配 | | `type` | Integer | 否 | 类型:1=按公司,2=按班组 | | `startDate` | String | 否 | 统计开始日期(格式:yyyy-MM-dd,返回开始日期>=该值的记录) | | `endDate` | String | 否 | 统计结束日期(格式:yyyy-MM-dd,返回结束日期<=该值的记录) | ### 9.3 响应字段 | 字段 | 类型 | 说明 | |------|------|------| | `id` | Long | 记录ID | | `companyId` | Long | 公司/班组ID | | `companyName` | String | 公司/班组名称 | | `type` | Integer | 类型:1=按公司,2=按班组 | | `startDate` | String | 统计开始日期 | | `endDate` | String | 统计结束日期 | | `workOrderCount` | Integer | 工单完成数量 | | `inspectionCount` | Integer | 巡查记录数量 | | `maintenanceCount` | Integer | 养护记录数量 | | `remark` | String | 备注 | | `createTime` | String | 创建时间 | ### 9.4 请求示例 ```bash curl -X GET "https://test.jichengshanshui.com.cn:28302/open-api/full-order-statistics/query?type=1&startDate=2026-06-01&endDate=2026-06-30" \ -H "appId: my_app_001" \ -H "timestamp: 1749696000000" \ -H "nonce: f6g7h8i9j0k1l2m3" \ -H "sign: ..." ``` ### 9.5 响应示例 ```json { "code": 0, "msg": "成功", "data": [ { "id": 1, "companyId": 100, "companyName": "养护一班", "type": 1, "startDate": "2026-06-01", "endDate": "2026-06-30", "workOrderCount": 85, "inspectionCount": 120, "maintenanceCount": 60, "remark": "二季度统计", "createTime": "2026-06-11 10:00:00" } ] } ``` --- ## 11. 错误码说明 ### 11.1 签名相关错误 | code | msg | 说明 | |------|-----|------| | 400 | 签名不正确 | 签名验证失败(appId不存在/timestamp超时/nonce长度不足/签名不匹配) | | 900 | 存在重复请求 | nonce 已被使用(重放攻击检测) | ### 11.2 业务接口错误 | code | msg | 说明 | |------|-----|------| | 1-100-020-001 | 人员查询失败 | 人员查询内部错误 | | 1-100-020-002 | 班组查询失败 | 班组查询内部错误 | | 1-100-020-003 | 考勤查询失败 | 考勤查询内部错误 | | 1-100-020-004 | 工单查询失败 | 工单查询内部错误 | | 1-100-020-005 | 物料查询失败 | 物料查询内部错误 | | 1-100-020-006 | 团队考核查询失败 | 团队考核聚合查询内部错误 | | 1-900-005-001 | 全量工单统计不存在 | 记录不存在 | | 1-900-005-002 | 相同公司/班组同时间段统计已存在 | 重复数据校验 | ### 11.3 通用错误 | code | msg | 说明 | |------|-----|------| | 400 | 参数校验失败 | 请求参数格式或值不正确 | | 500 | 系统内部错误 | 服务器内部异常 | --- ## 12. 附录:客户端签名代码示例 ### 12.1 Java 示例 ```java import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.core.util.RandomUtil; import java.util.Map; import java.util.TreeMap; public class OpenApiClient { private static final String BASE_URL = "https://test.jichengshanshui.com.cn:28302"; private static final String APP_ID = "your_app_id"; private static final String APP_SECRET = "your_app_secret"; public static String call(String path, Map params) { String timestamp = String.valueOf(System.currentTimeMillis()); String nonce = RandomUtil.randomString(16); String sign = buildSign(params, timestamp, nonce); String url = BASE_URL + path; HttpResponse response = HttpRequest.get(url) .form(params) .header("appId", APP_ID) .header("timestamp", timestamp) .header("nonce", nonce) .header("sign", sign) .execute(); return response.body(); } private static String buildSign(Map params, String timestamp, String nonce) { StringBuilder sb = new StringBuilder(); // 1. 排序 query 参数值 if (params != null && !params.isEmpty()) { new TreeMap<>(params).values().stream() .map(String::valueOf).forEach(sb::append); } // 2. header 参数值(appId, nonce, timestamp 字母序) sb.append(APP_ID); sb.append(nonce); sb.append(timestamp); // 3. 追加 appSecret + SHA256 sb.append(APP_SECRET); return DigestUtil.sha256Hex(sb.toString()); } } ``` ### 12.2 Python 示例 ```python import hashlib import time import random import string import requests BASE_URL = "https://test.jichengshanshui.com.cn:28302" APP_ID = "your_app_id" APP_SECRET = "your_app_secret" def build_sign(params, timestamp, nonce): parts = [] if params: for key in sorted(params.keys()): parts.append(str(params[key])) parts.append(APP_ID) parts.append(nonce) parts.append(timestamp) parts.append(APP_SECRET) sign_str = "".join(parts) return hashlib.sha256(sign_str.encode()).hexdigest() def call(path, params=None): if params is None: params = {} timestamp = str(int(time.time() * 1000)) nonce = "".join(random.choices(string.ascii_lowercase + string.digits, k=16)) sign = build_sign(params, timestamp, nonce) headers = { "appId": APP_ID, "timestamp": timestamp, "nonce": nonce, "sign": sign } response = requests.get(BASE_URL + path, params=params, headers=headers) return response.json() ``` ### 12.3 Shell (cURL) 示例 ```bash #!/bin/bash APP_ID="your_app_id" APP_SECRET="your_app_secret" TIMESTAMP=$(date +%s%3N) NONCE=$(openssl rand -hex 16) # 示例:/open-api/staff/query?deptId=100 # query params 排序后 values: deptId=100 → "100" QUERY_VALUES="100" HEADER_VALUES="${APP_ID}${NONCE}${TIMESTAMP}" SIGN_STR="${QUERY_VALUES}${HEADER_VALUES}${APP_SECRET}" SIGN=$(echo -n "$SIGN_STR" | openssl dgst -sha256 -hex | awk '{print $2}') curl -X GET "https://test.jichengshanshui.com.cn:28302/open-api/staff/query?deptId=100" \ -H "appId: ${APP_ID}" \ -H "timestamp: ${TIMESTAMP}" \ -H "nonce: ${NONCE}" \ -H "sign: ${SIGN}" ``` --- > **本文档基于 2026-06-11 代码生成,如有接口变更请同步更新。**