Files
water/docs/AppController-API.md
yuhaiming 70e2356f22 fix(app): 修复 AppController 安全与查询问题
- 加强异常处理、类型安全和图片上传校验
- 优化设备相关查询,避免重复访问数据源
- 补充并记录并发测试与审查修复实施计划
2026-07-17 08:20:44 +08:00

43 KiB
Raw Blame History

Water IoT APP 接口文档

业务接口 Base URL: /app/v1 认证接口前缀: /auth/resource 认证方式: Sa-Token/app/v1/** 业务接口需携带 Authorization: Bearer <token>,登录/注册/普通验证码接口免登录 响应编码: UTF-8 最后更新: 2026-07-14


目录


通用说明

统一响应结构 R<T>

大多数接口返回统一的 R<T> 包装结构:

{
  "code": 200,
  "msg": "操作成功",
  "data": { }
}
字段 类型 说明
code int 200 表示成功,500 表示失败
msg string 提示消息(支持 i18n 国际化)
data T 业务数据,类型视接口而定;无数据时为 null

判断逻辑code === 200 为成功,其余均为失败。


分页响应结构 TableDataInfo<T>

列表查询接口返回 TableDataInfo<T>

{
  "code": 200,
  "msg": "查询成功",
  "total": 100,
  "rows": [ ]
}
字段 类型 说明
code int 200 表示成功
msg string 提示消息
total long 总记录数
rows T[] 当前页数据列表

分页请求参数

所有分页查询接口GET 请求)支持以下 Query 参数:

参数 类型 必填 说明
pageNum int 当前页码,默认 1
pageSize int 每页条数,默认查全部

注意:设备列表、排程列表、浇水记录列表的排序方向固定为 createTime desc(服务端强制设置),前端无需传排序参数。


特殊标注说明

标注 说明
@ApiEncrypt 启用 api-decrypt 后强制请求体加密;默认配置关闭时仍使用普通 JSON
@RepeatSubmit 防重复提交,相同请求在短时间内只能提交一次
@Log 操作会被记录到系统日志

当前默认配置 api-decrypt.enabled=false,因此 @ApiEncrypt 接口可直接发送普通 JSON。 当部署环境启用接口加密后,标注 @ApiEncrypt 的 POST/PUT 接口必须携带 encrypt-key 请求头并发送加密后的 body该标注不代表免登录。


零、登录、注册与验证码

本章节接口来自 AuthControllerCaptchaController。两个控制器使用了 @SaIgnore,普通登录、注册和验证码接口免登录;退出登录、账号注销验证码和账号注销仍会在方法内部校验 Token。

默认 APP 客户端配置:

参数 默认值 说明
clientId 428a8310cd442757ae699df5d894f051 APP 客户端 ID来源于 sys_client 初始化数据
tenantId 000000 默认租户 ID关闭多租户时仍可沿用该值
userType app_user APP 注册用户类型
grantType password / sms 默认 APP 客户端支持密码、短信和社交授权

0.1 获取图形验证码

GET /auth/code

认证:免登录。验证码开启时按 IP 限流,每 60 秒最多 10 次。

响应R<CaptchaVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "captchaEnabled": true,
    "uuid": "54ef90b79e384d29a44d59f863f83a52",
    "img": "data:image/png;base64,..."
  }
}
字段 类型 说明
captchaEnabled boolean 是否启用图形验证码
uuid string 验证码缓存标识
img string Base64 图片数据

当前密码登录和注册服务中的图形验证码校验代码已注释APP 注册实际使用手机号/邮箱验证码。


0.2 获取通用手机号或邮箱验证码

根据 username 格式自动选择短信或邮件发送,主要用于注册和找回密码。

GET /resource/code?username={username}

认证:免登录。相同 username 每 60 秒最多请求 1 次。

参数 类型 必填 说明
username string 手机号或邮箱;手机号发送短信,其他值按邮箱发送

响应R<Void>

{
  "code": 200,
  "msg": "123456",
  "data": null
}

当前实现会把验证码同时放入响应 msg,生产环境存在验证码泄露风险,建议生产部署时改为固定提示文本。


0.3 获取短信验证码

用于短信验证码登录。

GET /resource/sms/code?phonenumber={phonenumber}

认证:免登录。相同手机号每 60 秒最多请求 1 次。

参数 类型 必填 说明
phonenumber string 接收验证码的手机号

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

0.4 获取邮箱验证码

GET /resource/email/code?email={email}

认证:免登录。相同邮箱每 60 秒最多请求 1 次。

参数 类型 必填 说明
email string 接收验证码的邮箱地址

响应R<Void>。未开启邮件功能时返回 当前系统没有开启邮箱功能!


0.5 用户登录

POST /auth/login

认证:免登录。接口标注 @ApiEncrypt,加密规则参见通用说明。

密码登录请求体grantType=password

字段 类型 必填 说明
clientId string APP 客户端 ID
grantType string 固定为 password
username string 用户名、手机号或邮箱,长度 2~30
password string 密码,长度 5~30
{
  "clientId": "428a8310cd442757ae699df5d894f051",
  "grantType": "password",
  "username": "13800000000",
  "password": "123456"
}

短信登录请求体grantType=sms

字段 类型 必填 说明
clientId string APP 客户端 ID
grantType string 固定为 sms
phonenumber string 手机号
smsCode string /resource/sms/code 获取的验证码
{
  "clientId": "428a8310cd442757ae699df5d894f051",
  "grantType": "sms",
  "phonenumber": "13800000000",
  "smsCode": "123456"
}

响应R<LoginVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "access_token": "登录Token",
    "refresh_token": null,
    "expire_in": 1800,
    "refresh_expire_in": null,
    "client_id": "428a8310cd442757ae699df5d894f051",
    "scope": null,
    "openid": null
  }
}

后续业务请求头:

Authorization: Bearer 登录Token

0.6 用户注册

POST /auth/register

认证:免登录。接口标注 @ApiEncrypt;系统配置必须允许注册。

字段 类型 必填 说明
clientId string APP 客户端 ID
grantType string 可传 password、sms,用于通过基础请求校验
username string 手机号、邮箱或用户名,长度 2~30
password string 密码,长度 5~30
userType string APP 用户固定传 app_user
code string 调用 /resource/code?username=... 获取的验证码
{
  "clientId": "428a8310cd442757ae699df5d894f051",
  "grantType": "password",
  "username": "13800000000",
  "password": "123456",
  "userType": "app_user",
  "code": "123456"
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

注册验证码以 username 为缓存键,因此获取验证码和注册时的 username 必须完全一致。


0.7 忘记密码

PUT /auth/forgot

认证:免登录。接口标注 @ApiEncrypt

字段 类型 必填 说明
username string 已注册的用户名或手机号
smsCode string username 为缓存键的验证码,可通过 /resource/code 获取
password string 新密码,长度 5~30
{
  "username": "13800000000",
  "smsCode": "123456",
  "password": "NewPass456"
}

响应R<Void>

当前实现限制:邮箱分支查询结果未赋值给用户对象,因此邮箱找回密码当前会被判定为“账号未注册”;建议修复后再开放邮箱找回。


0.8 退出登录

POST /auth/logout

认证:需要 Authorization: Bearer <token>

响应R<Void>,成功消息为 退出成功


0.9 获取账号注销验证码

根据当前登录账号优先向已绑定手机号发送短信;没有有效手机号时发送到邮箱。

GET /resource/account/cancel/code

认证:需要 Authorization: Bearer <token>。每个用户每 60 秒最多请求 1 次。

响应R<Void>

验证码以当前登录用户名为缓存键。账号未绑定手机号或邮箱时返回失败。


0.11 注销账号

DELETE /auth/account

认证:需要 Authorization: Bearer <token>

字段 类型 必填 说明
code string /resource/account/cancel/code 获取的验证码
{
  "code": "123456"
}

响应R<Void>,成功消息为 注销成功

注销成功后会解绑并初始化用户名下设备,删除排程、浇水记录及系统用户关联数据,并退出当前账号。该操作不可恢复,超级管理员账号不允许注销。


一、设备管理

1.1 查询设备列表(分页)

查询当前登录用户名下的设备列表。

GET /app/v1/deviceList

请求参数Query

参数 类型 必填 说明
pageNum int 页码,默认 1
pageSize int 每页条数
deviceNo string 设备编号(精确查询)
deviceName string 设备名称(模糊查询)
status string 设备状态:1-在线 0-离线 2-到期 3-故障
wifiName string WiFi 名称(模糊查询)
deviceEm string 设备型号(精确查询)
deviceSn string 设备序列号(精确查询)
fwVer string 固件版本(精确查询)
macAddress string MAC 地址(精确查询)

响应TableDataInfo<AppDeviceVo>

{
  "code": 200,
  "msg": "查询成功",
  "total": 2,
  "rows": [
    {
      "deviceNo": "D20260101001",
      "userId": 1,
      "deviceName": "前院浇灌器",
      "deviceInitName": "智能浇灌器Pro",
      "deviceImg": "https://oss.example.com/device/xxx.png",
      "qrcode": "D20260101001",
      "status": "1",
      "workStatus": "0",
      "powerLevel": "85",
      "powerLevelUpdatatime": "2026-07-14 08:30:00",
      "wifiName": "Home-WiFi",
      "wifiPassword": "12345678",
      "deviceEm": "WATER-PRO-1",
      "deviceSn": "SN20260101001",
      "fwVer": "1.0.3",
      "macAddress": "AA:BB:CC:DD:EE:FF",
      "nickName": "张三",
      "expirationTime": "2027-01-01 00:00:00"
    }
  ]
}

1.2 绑定设备状态检查

检查设备绑定状态(加密接口)。

POST /app/v1/bindDeviceStatus

请求头

Content-Type: application/json

请求体JSON启用接口加密时发送加密后的 JSON 字符串)

字段 类型 必填 说明
deviceNo string 设备编号
macAddress string MAC 地址
deviceInitName string 设备初始名称

以上三个设备标识至少填写一个。查询顺序为 deviceNomacAddressdeviceInitName

响应R<Map<String, Object>>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "bindDeviceStatus": 304,
    "bindDeviceStatusName": "设备未绑定,请先绑定用户",
    "bindDevice": {
      "deviceNo": "D20260101001",
      "macAddress": "AA:BB:CC:DD:EE:FF",
      "userId": null,
      "status": "1",
      "workStatus": "2"
    }
  }
}

bindDeviceStatus 业务状态码:

状态码 说明
200 设备已绑定当前用户,可正常使用
300 设备信息为空
301 未提供设备编号、MAC 地址或设备初始名称
302 设备未入库注册
303 设备已被其他用户绑定
304 设备存在但尚未绑定用户,可以继续调用 /addDevice
305 设备尚未完成配网

这些是 data.bindDeviceStatus 的业务状态码;接口外层 code 在正常处理时仍为 200


1.3 绑定已上线设备

绑定通过 MQTT 注册上线的设备到当前用户(加密接口)。

POST /app/v1/addDevice

请求头

Content-Type: application/json

请求体JSON启用接口加密时发送加密后的 JSON 字符串)

字段 类型 必填 说明
deviceNo string MQTT 注册后服务端分配的设备编号
deviceName string 设备名称
status string 绑定后保留/设置的设备在线状态
wifiName string WiFi 名称
wifiPassword string WiFi 密码

响应R<AppDeviceVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "deviceNo": "D20260101001",
    "userId": 1,
    "deviceName": "前院浇灌器",
    "deviceInitName": "智能浇灌器Pro",
    "deviceImg": "https://oss.example.com/device/xxx.png",
    "status": "1",
    "workStatus": "0",
    "macAddress": "AA:BB:CC:DD:EE:FF",
    "expirationTime": "2027-01-01 00:00:00"
  }
}

1.4 获取设备详情

GET /app/v1/device/{deviceNo}

路径参数

参数 类型 必填 说明
deviceNo string 设备编号

响应R<AppDeviceVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "deviceNo": "D20260101001",
    "userId": 1,
    "deviceName": "前院浇灌器",
    "deviceInitName": "智能浇灌器Pro",
    "deviceImg": "https://oss.example.com/device/xxx.png",
    "qrcode": "D20260101001",
    "status": "1",
    "workStatus": "0",
    "powerLevel": "85",
    "powerLevelUpdatatime": "2026-07-14 08:30:00",
    "wifiName": "Home-WiFi",
    "wifiPassword": "12345678",
    "deviceEm": "WATER-PRO-1",
    "deviceSn": "SN20260101001",
    "fwVer": "1.0.3",
    "macAddress": "AA:BB:CC:DD:EE:FF",
    "nickName": "张三",
    "expirationTime": "2027-01-01 00:00:00"
  }
}

权限:只能查询当前用户名下的设备,否则返回错误。


1.5 修改设备信息

PUT /app/v1/updateDeviceInfo

@RepeatSubmit — 防重复提交

请求头

Content-Type: application/json

请求体AppDeviceBoJSON

字段 类型 必填 说明
deviceNo string 设备编号EditGroup 校验)
deviceName string 设备名称
deviceImg string 设备图片 URL
wifiName string WiFi 名称
wifiPassword string WiFi 密码
{
  "deviceNo": "D20260101001",
  "deviceName": "后院浇灌器",
  "deviceImg": "https://oss.example.com/device/new.png"
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

权限:只能修改当前用户名下的设备。


1.6 删除设备(解绑)

解绑一个或多个设备。

DELETE /app/v1/deleteDevice/{deviceNos}

路径参数

参数 类型 必填 说明
deviceNos string 设备编号,多个用逗号分隔。例:D001,D002

示例

DELETE /app/v1/deleteDevice/D20260101001,D20260101002

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

权限:只能解绑当前用户名下的设备。


1.7 手动开关设备

手动开启或关闭设备工作状态,并可通过 MQTT 下发指令到设备。

PUT /app/v1/switchDevice

@RepeatSubmit — 防重复提交

请求头

Content-Type: application/json

请求体JSON

字段 类型 必填 说明
deviceNo string 设备编号
workStatus string 工作状态:1-开启 0-关闭
durationMin int 条件必填 持续时长(分钟);workStatus=1 时必须大于 0,关闭时可不传
{
  "deviceNo": "D20260101001",
  "workStatus": "1",
  "durationMin": 20
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

说明:开启时 startTime 由服务端按当前时间生成,格式为 yyyy-MM-dd HH:mm:ss


二、排程管理

2.1 查询排程列表(分页)

GET /app/v1/schedulelist

请求参数Query

参数 类型 必填 说明
pageNum int 页码
pageSize int 每页条数
name string 排程名称(模糊查询)
status string 状态:0-关闭 1-开启

响应TableDataInfo<AppScheduleVo>

{
  "code": 200,
  "msg": "查询成功",
  "total": 1,
  "rows": [
    {
      "id": 1,
      "userId": 1,
      "name": "工作日早晨浇水",
      "status": "1",
      "details": null,
      "deviceNos": null
    }
  ]
}

注意:列表查询返回的 detailsdeviceNosnull,完整信息请通过 2.3 排程详情 获取。


2.2 新增排程

POST /app/v1/addschedule

@RepeatSubmit — 防重复提交

请求头

Content-Type: application/json

请求体AppScheduleBoJSONAddGroup 校验)

字段 类型 必填 说明
name string 排程名称
status string 状态:0-关闭 1-开启
details array 排程详情列表(至少一天)

details 数组元素结构(AppScheduleDetail

字段 类型 必填 说明
weekday int 星期几:1-周一 ~ 7-周日
timeData array 时间段列表
zones int 浇水区域(位掩码)
triggerType string 触发类型:0-排程(默认)
status string 状态:0-关闭(默认)

timeData 数组元素结构(TimeSlot

字段 类型 必填 说明
startTime string 开始时间,格式 HH:mm
durationMin int 持续时长(分钟)
{
  "name": "工作日早晨浇水",
  "status": "1",
  "details": [
    {
      "weekday": 1,
      "timeData": [
        { "startTime": "06:30", "durationMin": 15 },
        { "startTime": "18:00", "durationMin": 20 }
      ],
      "triggerType": "0",
      "status": "1"
    },
    {
      "weekday": 2,
      "timeData": [
        { "startTime": "06:30", "durationMin": 15 }
      ],
      "triggerType": "0",
      "status": "1"
    }
  ]
}

响应R<AppScheduleVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "id": 10,
    "userId": 1,
    "name": "工作日早晨浇水",
    "status": "1",
    "details": [
      {
        "id": 101,
        "weekday": 1,
        "timeData": [
          { "startTime": "06:30", "durationMin": 15 },
          { "startTime": "18:00", "durationMin": 20 }
        ],
        "triggerType": "0",
        "status": "1"
      }
    ],
    "deviceNos": null
  }
}

2.3 排程详情

获取排程完整信息,包含排程详情列表和已绑定设备列表。

GET /app/v1/schedule/{id}

路径参数

参数 类型 必填 说明
id long 排程 ID

响应R<AppScheduleVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "id": 10,
    "userId": 1,
    "name": "工作日早晨浇水",
    "status": "1",
    "details": [
      {
        "id": 1,
        "weekday": 1,
        "timeData": [
          { "startTime": "06:30", "durationMin": 15 },
          { "startTime": "18:00", "durationMin": 20 }
        ],
        "triggerType": "0",
        "status": "1"
      }
    ],
    "deviceNos": [
      {
        "deviceNo": "D20260101001",
        "userId": 1,
        "deviceName": "前院浇灌器",
        "status": "1",
        "workStatus": "0"
      }
    ]
  }
}

说明

  • details 返回该排程当前查询到的全部明细,不在控制器中额外过滤状态。
  • deviceNos 为已绑定设备的完整 AppDeviceVo 列表。
  • timeData 已从 JSON 字符串解析为数组。

2.4 修改排程状态

开启或关闭排程,状态变更后通过 MQTT 通知所有已绑定设备。

PUT /app/v1/editScheduleStatus

请求头

Content-Type: application/json

请求体AppScheduleBoJSON

字段 类型 必填 说明
id long 排程 ID
status string 目标状态:0-关闭 1-开启
{
  "id": 10,
  "status": "0"
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

MQTT 通知

  • 关闭(status=0):向绑定设备下发排程取消指令。
  • 开启(status=1):向绑定设备重新下发完整排程信息。

2.5 修改排程

修改排程内容,更新后通过 MQTT 向所有绑定设备重新下发排程。

PUT /app/v1/updataschedule

@RepeatSubmit — 防重复提交

请求头

Content-Type: application/json

请求体AppScheduleBoJSONEditGroup 校验)

字段 类型 必填 说明
id long 排程 IDEditGroup 校验)
name string 排程名称
status string 状态:0-关闭 1-开启
details array 排程详情列表(同 2.2 新增排程
{
  "id": 10,
  "name": "周末浇水计划",
  "status": "1",
  "details": [
    {
      "id": 101,
      "weekday": 6,
      "timeData": [
        { "startTime": "08:00", "durationMin": 30 }
      ],
      "triggerType": "0",
      "status": "1"
    }
  ]
}

响应R<Object>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "id": 10,
    "userId": 1,
    "name": "周末浇水计划",
    "status": "1",
    "details": [ ]
  }
}

说明data 为更新后的 AppScheduleVo 对象。修改已有排程明细时应传明细 id,否则服务端无法按主键更新该明细。


2.6 删除排程

删除一个或多个排程,并通知所有绑定设备解绑。

DELETE /app/v1/deleteschedule/{ids}

路径参数

参数 类型 必填 说明
ids long 排程 ID多个用逗号分隔。例10,11

示例

DELETE /app/v1/deleteschedule/10,11

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

MQTT 通知:删除成功后,向每个排程关联的所有设备下发解绑指令。


三、排程-设备关联

3.1 查询排程可关联的设备

查询当前用户名下未被其他排程关联的设备列表。

GET /app/v1/scheduleDeviceList/{scheduleId}

路径参数

参数 类型 必填 说明
scheduleId long 排程 ID

响应R<Map<String, Object>>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "deviceList": [
      {
        "deviceNo": "D20260101003",
        "userId": 1,
        "deviceName": "阳台浇灌器",
        "status": "1",
        "workStatus": "0"
      }
    ]
  }
}

说明:返回的设备列表为当前用户名下未与任何排程关联的设备。已关联其他排程的设备不会出现在列表中。


3.2 绑定排程与设备

将一个或多个设备绑定到指定排程,绑定后通过 MQTT 向设备下发排程信息。

POST /app/v1/addScheduleDevice

@RepeatSubmit — 防重复提交

请求头

Content-Type: application/json

请求体JSON

字段 类型 必填 说明
scheduleId long 排程 ID
deviceNos array 设备编号列表。也可用 deviceNo(逗号分隔字符串)或 deviceIds
{
  "scheduleId": 10,
  "deviceNos": ["D20260101001", "D20260101003"]
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

说明

  • deviceNos 支持数组或逗号分隔字符串格式。
  • 已绑定该排程的设备会自动跳过(幂等)。
  • 绑定后,服务端通过 MQTT 向设备下发排程详情。

3.3 删除排程与设备绑定

解除排程与设备的绑定关系,并通知设备取消排程。

DELETE /app/v1/deleteScheduleDevice

请求参数Query

参数 类型 必填 说明
scheduleId string 排程 ID
deviceNo string 设备编号

示例

DELETE /app/v1/deleteScheduleDevice?scheduleId=10&deviceNo=D20260101001

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

MQTT 通知:解绑成功后,向设备下发排程解绑指令。若 MQTT 下发失败,不影响接口返回成功(仅记录日志警告)。


四、浇水记录

4.1 查询浇水记录列表(分页)

GET /app/v1/waterLogList

请求参数Query

参数 类型 必填 说明
pageNum int 页码
pageSize int 每页条数
deviceNo string 设备编号
commandId string MQTT 命令 ID
scheduleId long 关联排程 ID
startTime date 开始时间(精确匹配)
endTime date 结束时间(精确匹配)
durationMin string 持续时间(精确匹配)
zones string 浇水区域
triggerType string 触发类型:0-排程 1-手动
remark string 备注(模糊匹配)
params[beginTime] date 创建时间范围开始;需与 params[endTime] 同时传入
params[endTime] date 创建时间范围结束;需与 params[beginTime] 同时传入

响应TableDataInfo<AppWateringLogVo>

{
  "code": 200,
  "msg": "查询成功",
  "total": 50,
  "rows": [
    {
      "id": 1,
      "userId": 1,
      "deviceNo": "D20260101001",
      "deviceName": "前院浇灌器",
      "commandId": "CMD_20260714001",
      "scheduleId": 10,
      "startTime": "2026-07-14 06:30:00",
      "endTime": "2026-07-14 06:45:00",
      "durationMin": "15",
      "zones": "1",
      "triggerType": "0",
      "remark": null,
      "status": "0",
      "createTime": "2026-07-14 06:30:00"
    }
  ]
}

说明:响应中 deviceName 由服务端关联查询后填充。


4.2 查询设备最新进行中的浇水记录

GET /app/v1/latestWaterLog

请求参数Query

参数 类型 必填 说明
deviceNo string 设备编号

响应R<Map<String, Object>>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "startTime": "2026-07-14 08:00:00",
    "endTime": "2026-07-14 08:20:00"
  }
}

说明:控制器按当前用户、设备编号和状态 1 发起查询,并取 ID 最大的一条记录。若无进行中的记录,datanull。时间格式为 yyyy-MM-dd HH:mm:ss

当前实现限制AppWateringLogServiceImpl.buildQueryWrapper 尚未把 status 加入查询条件,因此现有代码实际可能取到该设备 ID 最大但已结束的记录。客户端依赖“进行中”语义前,应先修复该服务查询条件。


4.3 删除浇水记录

DELETE /app/v1/waterLog/{ids}

路径参数

参数 类型 必填 说明
ids long 记录 ID多个用逗号分隔。例1,2,3

示例

DELETE /app/v1/waterLog/1,2,3

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

权限:只能删除当前用户的浇水记录。


五、数据统计

5.1 数据统计

获取当前用户的浇水数据统计汇总。

GET /app/v1/dataStatistics

请求参数:无

响应R<Map<String, Object>>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "count": 120,
    "monCount": 15,
    "weekCount": 4,
    "schedule": 80,
    "manualNum": 40,
    "allTime": 3600,
    "averageTime": 30
  }
}
字段 类型 说明
count long 总浇水次数
monCount long 本月浇水次数
weekCount long 本周浇水次数
schedule long 排程触发浇水次数
manualNum long 手动触发浇水次数
allTime long 总浇水时间(分钟)
averageTime long 平均每次浇水时间(分钟)

六、用户管理

6.1 获取用户信息

GET /app/v1/getUserInfo

请求参数:无

响应R<UserInfoVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "user": {
      "userId": 1,
      "deptId": 100,
      "userName": "admin",
      "nickName": "管理员",
      "email": "admin@example.com",
      "phonenumber": "138****8888",
      "sex": "0",
      "avatar": "https://oss.example.com/avatar/xxx.png",
      "status": "0",
      "loginIp": "192.168.1.1",
      "loginDate": "2026-07-14 09:00:00",
      "remark": null,
      "createTime": "2026-01-01 00:00:00"
    },
    "permissions": ["*:*:*"],
    "roles": ["admin"]
  }
}

说明phonenumberemail 字段做了脱敏处理(需 system:user:edit 权限才能看到完整内容)。


6.2 修改用户信息

PUT /app/v1/updataUser

@RepeatSubmit — 防重复提交

请求头

Content-Type: application/json

请求体SysUserBoJSON

字段 类型 必填 说明
userId long 用户 ID
nickName string 用户昵称(最长 30 字符)
email string 邮箱(需符合邮箱格式,最长 50 字符)
phonenumber string 手机号(需唯一)
code string 条件必填 修改手机号时使用的短信验证码
sex string 性别:0-男 1-女 2-未知
avatar string 头像 URL
{
  "userId": 1,
  "nickName": "新昵称",
  "phonenumber": "13900001111",
  "code": "123456",
  "email": "new@example.com",
  "sex": "0"
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

校验:手机号需全局唯一;请求中包含 phonenumber 时,服务层会校验 code 短信验证码。


6.3 修改登录密码

PUT /app/v1/updataPassword

@RepeatSubmit — 防重复提交

请求头

Content-Type: application/json

请求体SysUserVoJSON

字段 类型 必填 说明
oldPassword string 旧密码(明文,服务端 BCrypt 校验)
password string 新密码(明文)
{
  "oldPassword": "OldPass123",
  "password": "NewPass456"
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

说明:旧密码使用 BCrypt 校验,不匹配则返回 user.password.not.match 错误。


6.4 找回密码

通过手机号或邮箱找回密码(加密接口)。

POST /app/v1/retrievePassword

请求头

Content-Type: application/json

请求体JSON启用接口加密时发送加密后的 JSON 字符串)

字段 类型 必填 说明
userName string 用户名(手机号或邮箱)
password string 新密码
code string 条件必填 userName 为手机号时使用的短信验证码
{
  "userName": "13900001111",
  "password": "NewPass456",
  "code": "123456"
}

响应R<Void>

{
  "code": 200,
  "msg": "操作成功",
  "data": null
}

校验逻辑

  • userName 为邮箱格式:校验邮箱是否已注册,未注册则报错 user.email.not.username
  • userName 为手机号格式:校验手机号是否已注册及短信验证码,未注册则报错 user.mobile.phone.number.not.username
  • 当前实现仍从登录 Token 获取 userId,因此该接口不是匿名接口,并且控制器内未校验短信/邮箱验证码。

七、文件上传

7.1 上传图片

上传图片到 OSS 对象存储,仅支持图片类型。

POST /app/v1/uploadImage

请求头

Content-Type: multipart/form-data

请求参数FormData

参数 类型 必填 说明
file file 图片文件Content-Type 必须以 image/ 开头)

当前 Spring Multipart 配置允许单文件最大 100MB、单次请求最大 100MB;实际部署还可能受网关和 OSS 配置限制。

响应R<SysOssUploadVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "url": "https://oss.example.com/2026/07/14/xxx.png",
    "fileName": "screenshot.png",
    "ossId": "123456"
  }
}
字段 类型 说明
url string 文件访问 URL
fileName string 原始文件名
ossId string 对象存储记录 ID

八、版本检查

8.1 检查APP版本更新

GET /app/v1/checkVersion

请求参数Query

参数 类型 必填 说明
platform string 平台类型:android(默认)或 ios
currentVersion string APP 当前版本号

示例

GET /app/v1/checkVersion?platform=android&currentVersion=1.0.2

响应R<AppVersionCheckVo>

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "platform": "android",
    "currentVersion": "1.0.2",
    "latestVersion": "1.0.3",
    "versionCode": 3,
    "updateAvailable": true,
    "forceUpdate": false,
    "downloadUrl": "https://oss.example.com/app/water-1.0.3.apk",
    "releaseNotes": "1. 修复设备绑定问题\n2. 优化排程功能"
  }
}
字段 类型 说明
platform string 平台类型
currentVersion string 客户端当前版本
latestVersion string 服务端最新版本
versionCode int 版本序号(用于排序)
updateAvailable boolean 是否有可更新版本
forceUpdate boolean 是否强制更新
downloadUrl string 安装包下载地址
releaseNotes string 更新说明

当前版本判断采用版本字符串是否完全相等:currentVersionlatestVersion 不相等时,updateAvailable=true,未进行语义化版本大小比较。


九、内部调试接口

9.1 MQTT绑定命令测试

GET /app/v1/test

该接口会直接调用 sendBindDeviceCommand("01"),向固定设备编号 01 下发绑定命令,并返回生成的命令 ID。

响应R<Void>(运行时实际将命令 ID 作为消息文本返回)

{
  "code": 200,
  "msg": "生成的命令ID",
  "data": null
}

该路由仅用于后端联调,不属于 APP 正式业务接口。生产环境建议删除、关闭或增加管理员权限控制。


附录:数据模型

AppDeviceVo设备信息

字段 类型 说明
deviceNo string 设备编号
userId long 用户 ID
deviceName string 设备名称
deviceInitName string 设备初始名称
deviceImg string 设备图片 URL
qrcode string 二维码内容
status string 设备状态:1-在线 0-离线 2-到期 3-故障
workStatus string 工作状态:0-休息 1-工作 2-初始/未配网
powerLevel string 电量百分比
powerLevelUpdatatime date 电量更新时间
wifiName string WiFi 名称
wifiPassword string WiFi 密码
deviceEm string 设备型号
deviceSn string 设备序列号
fwVer string 固件版本
macAddress string MAC 地址
nickName string 用户昵称
expirationTime date 到期时间

AppScheduleVo排程信息

字段 类型 说明
id long 排程 ID
userId long 用户 ID
name string 排程名称
status string 状态:0-关闭 1-开启
details array 排程详情列表;新增和详情接口会返回,列表接口通常为空
deviceNos array 已绑定设备列表(仅详情接口返回)

AppScheduleDetail排程详情

字段 类型 说明
id long 详情 ID
weekday int 星期几:1-周一 ~ 7-周日
timeData array 时间段列表
triggerType string 触发类型:0-排程 1-手动
status string 状态:0-关闭 1-开启

TimeSlot时间段

字段 类型 说明
startTime string 开始时间(格式 HH:mm
durationMin int 持续时长(分钟)

AppWateringLogVo浇水记录

字段 类型 说明
id long 记录 ID
userId long 用户 ID
deviceNo string 设备编号
deviceName string 设备名称
commandId string 命令 ID
scheduleId long 关联排程 ID
startTime date 开始时间
endTime date 结束时间
durationMin string 持续时间(分钟)
zones string 浇水区域
triggerType string 触发类型:0-排程 1-手动
remark string 备注
status string 状态:0-已结束 1-进行中
createTime date 创建时间

枚举值速查

枚举 说明
设备状态 status 1 在线
0 离线
2 到期
3 故障
工作状态 workStatus 0 休息
1 工作
2 初始/未配网
排程状态 status 0 关闭
1 开启
触发类型 triggerType 0 排程触发
1 手动触发
浇水记录状态 status 0 已结束
1 进行中
星期 weekday 1~7 周一~周日
用户性别 sex 0
1
2 未知
强制更新 forceUpdate 0
1

联调提示

  1. /app/v1/** 业务接口及明确标注“需要登录”的认证接口,请求头需携带 Authorization: Bearer <token>;登录、注册、忘记密码、租户列表和普通验证码接口免登录。
  2. 当前 api-decrypt.enabled=false 时发送普通 JSON启用后标注 @ApiEncrypt 的接口(绑定设备状态检查、绑定设备、找回密码)需按约定加密请求体并携带 encrypt-key
  3. 标注 @RepeatSubmit 的接口,短时间内重复提交会被拦截,前端应在收到响应前禁用提交按钮。
  4. 删除类接口的路径参数支持逗号分隔多个 ID。
  5. 分页接口排序固定为 createTime desc,前端无需传排序参数。
  6. 时间字段统一使用 yyyy-MM-dd HH:mm:ss 格式,时间段 startTime 使用 HH:mm 格式。