diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index 1bb8863ba..1d53e5059 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -153,3 +153,49 @@ COMMENT ON COLUMN mch_store_info.deleted IS '逻辑删除标志'; CREATE UNIQUE INDEX IF NOT EXISTS uk_mch_store_info_store_no ON mch_store_info (store_no); -- 商户号查询索引 CREATE INDEX IF NOT EXISTS idx_mch_store_info_mch_no ON mch_store_info (mch_no, deleted); + +-- =================================== +-- 云音响设备(商米云音响, 设备台账与商户/门店绑定关系) +-- =================================== +CREATE TABLE IF NOT EXISTS iot_speaker_device ( + id bigint NOT NULL, + mch_no varchar(32) NOT NULL, + device_sn varchar(64) NOT NULL, + imei varchar(32), + shop_id varchar(64), + device_name varchar(128), + status varchar(16) NOT NULL DEFAULT 'unbound', + bind_time timestamptz(6), + last_online_time timestamptz(6), + remark varchar(512), + creator bigint, + create_time timestamptz(6), + last_modifier bigint, + last_modified_time timestamptz(6), + version int NOT NULL DEFAULT 0, + deleted boolean NOT NULL DEFAULT false, + CONSTRAINT iot_speaker_device_pkey PRIMARY KEY (id) +); + +COMMENT ON TABLE iot_speaker_device IS '云音响设备(商米云音响, 设备台账与商户/门店绑定关系)'; +COMMENT ON COLUMN iot_speaker_device.id IS '主键'; +COMMENT ON COLUMN iot_speaker_device.mch_no IS '所属商户号'; +COMMENT ON COLUMN iot_speaker_device.device_sn IS '商米设备序列号(SN)'; +COMMENT ON COLUMN iot_speaker_device.imei IS '设备IMEI'; +COMMENT ON COLUMN iot_speaker_device.shop_id IS '商米门店ID'; +COMMENT ON COLUMN iot_speaker_device.device_name IS '设备名称'; +COMMENT ON COLUMN iot_speaker_device.status IS '设备状态(unbound未绑定/online在线/offline离线/fault故障)'; +COMMENT ON COLUMN iot_speaker_device.bind_time IS '绑定时间'; +COMMENT ON COLUMN iot_speaker_device.last_online_time IS '最后在线时间'; +COMMENT ON COLUMN iot_speaker_device.remark IS '备注'; +COMMENT ON COLUMN iot_speaker_device.creator IS '创建人ID'; +COMMENT ON COLUMN iot_speaker_device.create_time IS '创建时间'; +COMMENT ON COLUMN iot_speaker_device.last_modifier IS '最后修改人ID'; +COMMENT ON COLUMN iot_speaker_device.last_modified_time IS '最后修改时间'; +COMMENT ON COLUMN iot_speaker_device.version IS '版本号(乐观锁)'; +COMMENT ON COLUMN iot_speaker_device.deleted IS '逻辑删除标志'; + +-- 设备序列号唯一索引(未删除范围内) +CREATE UNIQUE INDEX IF NOT EXISTS uk_iot_speaker_device_sn ON iot_speaker_device (device_sn) WHERE deleted = false; +-- 商户号查询索引 +CREATE INDEX IF NOT EXISTS idx_iot_speaker_device_mch_no ON iot_speaker_device (mch_no, deleted); diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/iot/speaker/controller/IotSpeakerDeviceAdminController.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/iot/speaker/controller/IotSpeakerDeviceAdminController.java new file mode 100644 index 000000000..1aaa6c5a4 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/iot/speaker/controller/IotSpeakerDeviceAdminController.java @@ -0,0 +1,89 @@ +package cn.daxpay.open.payment.admin.iot.speaker.controller; + +import cn.daxpay.open.payment.admin.iot.speaker.service.IotSpeakerDeviceAdminService; +import cn.daxpay.open.payment.iot.speaker.param.IotSpeakerDeviceParam; +import cn.daxpay.open.payment.iot.speaker.param.IotSpeakerDeviceQuery; +import cn.daxpay.open.payment.iot.speaker.result.IotSpeakerDeviceResult; +import cn.daxpay.open.platform.core.annotation.PermCode; +import cn.daxpay.open.platform.core.rest.Res; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.core.rest.result.PageResult; +import cn.daxpay.open.platform.core.rest.result.Result; +import cn.daxpay.open.platform.core.validation.ValidationGroup; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/// # 云音响设备管理(运营端) +/// +@PermCode(menuCode = "payment:iot:speaker") +@Validated +@Tag(name = "云音响设备管理") +@RestController +@RequestMapping("/admin/iot/speaker-device") +@RequiredArgsConstructor +public class IotSpeakerDeviceAdminController { + + private final IotSpeakerDeviceAdminService iotSpeakerDeviceAdminService; + + @PermCode(code = "add", nameCn = "云音响新增", nameEn = "Speaker Add") + @Operation(summary = "新增云音响设备") + @PostMapping("/add") + public Result add(@RequestBody @Validated(ValidationGroup.add.class) IotSpeakerDeviceParam param) { + iotSpeakerDeviceAdminService.add(param); + return Res.ok(); + } + + @PermCode(code = "edit", nameCn = "云音响编辑", nameEn = "Speaker Edit") + @Operation(summary = "修改云音响设备") + @PostMapping("/update") + public Result update(@RequestBody @Validated(ValidationGroup.edit.class) IotSpeakerDeviceParam param) { + iotSpeakerDeviceAdminService.update(param); + return Res.ok(); + } + + @PermCode(code = "view", nameCn = "云音响查看", nameEn = "Speaker View") + @Operation(summary = "云音响设备分页") + @GetMapping("/page") + public Result> page(PageParam pageParam, IotSpeakerDeviceQuery query) { + return Res.ok(iotSpeakerDeviceAdminService.page(pageParam, query)); + } + + @PermCode(code = "view", nameCn = "云音响查看", nameEn = "Speaker View") + @Operation(summary = "根据id查询云音响设备") + @GetMapping("/get") + public Result findById(@NotNull(message = "{validation.field.id.notNull}") Long id) { + return Res.ok(iotSpeakerDeviceAdminService.findById(id)); + } + + @PermCode(code = "delete", nameCn = "云音响删除", nameEn = "Speaker Delete") + @Operation(summary = "删除云音响设备") + @PostMapping("/delete") + public Result delete(@NotNull(message = "{validation.field.id.notNull}") Long id) { + iotSpeakerDeviceAdminService.delete(id); + return Res.ok(); + } + + @PermCode(code = "edit", nameCn = "云音响编辑", nameEn = "Speaker Edit") + @Operation(summary = "绑定云音响设备") + @PostMapping("/bind") + public Result bind(@NotNull(message = "{validation.field.id.notNull}") Long id) { + iotSpeakerDeviceAdminService.bind(id); + return Res.ok(); + } + + @PermCode(code = "edit", nameCn = "云音响编辑", nameEn = "Speaker Edit") + @Operation(summary = "解绑云音响设备") + @PostMapping("/unbind") + public Result unbind(@NotNull(message = "{validation.field.id.notNull}") Long id) { + iotSpeakerDeviceAdminService.unbind(id); + return Res.ok(); + } +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/iot/speaker/service/IotSpeakerDeviceAdminService.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/iot/speaker/service/IotSpeakerDeviceAdminService.java new file mode 100644 index 000000000..f2913bce1 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/iot/speaker/service/IotSpeakerDeviceAdminService.java @@ -0,0 +1,110 @@ +package cn.daxpay.open.payment.admin.iot.speaker.service; + +import cn.daxpay.open.payment.iot.speaker.dao.IotSpeakerDeviceManager; +import cn.daxpay.open.payment.iot.speaker.entity.IotSpeakerDevice; +import cn.daxpay.open.payment.iot.speaker.enums.IotDeviceStatusEnum; +import cn.daxpay.open.payment.iot.speaker.param.IotSpeakerDeviceParam; +import cn.daxpay.open.payment.iot.speaker.param.IotSpeakerDeviceQuery; +import cn.daxpay.open.payment.iot.speaker.result.IotSpeakerDeviceResult; +import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil; +import cn.daxpay.open.platform.core.code.CommonCode; +import cn.daxpay.open.platform.core.exception.DataNotExistException; +import cn.daxpay.open.platform.core.exception.operation.OperationFailException; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.core.rest.result.PageResult; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.OffsetDateTime; + +/// # 云音响设备管理(运营端) +/// +/// 首期仅维护本地设备台账, 绑定/解绑只更新本地状态; 真实商米对接由独立服务 dax-pay-iot 完成。 +@Slf4j +@Service +@RequiredArgsConstructor +public class IotSpeakerDeviceAdminService { + + private final IotSpeakerDeviceManager iotSpeakerDeviceManager; + + /// 新增设备(默认未绑定状态) + @Transactional(rollbackFor = Exception.class) + public void add(IotSpeakerDeviceParam param) { + // 校验设备SN唯一 + if (iotSpeakerDeviceManager.existsByDeviceSn(param.getDeviceSn(), null)) { + // 云音响: 设备序列号已存在 + throw new OperationFailException(CommonCode.FAIL_CODE, "error.iot.speaker.deviceSnExists"); + } + IotSpeakerDevice entity = new IotSpeakerDevice() + .setMchNo(param.getMchNo()) + .setDeviceSn(param.getDeviceSn()) + .setImei(param.getImei()) + .setShopId(param.getShopId()) + .setDeviceName(param.getDeviceName()) + .setRemark(param.getRemark()) + // 新增默认未绑定 + .setStatus(IotDeviceStatusEnum.UNBOUND.getCode()); + iotSpeakerDeviceManager.save(entity); + } + + /// 修改设备 + @Transactional(rollbackFor = Exception.class) + public void update(IotSpeakerDeviceParam param) { + IotSpeakerDevice entity = iotSpeakerDeviceManager.findById(param.getId()) + // 云音响: 设备不存在 + .orElseThrow(() -> new DataNotExistException("error.iot.speaker.deviceNotFound")); + // SN 变更时校验唯一 + if (!entity.getDeviceSn().equals(param.getDeviceSn()) + && iotSpeakerDeviceManager.existsByDeviceSn(param.getDeviceSn(), param.getId())) { + throw new OperationFailException(CommonCode.FAIL_CODE, "error.iot.speaker.deviceSnExists"); + } + entity.setMchNo(param.getMchNo()) + .setDeviceSn(param.getDeviceSn()) + .setImei(param.getImei()) + .setShopId(param.getShopId()) + .setDeviceName(param.getDeviceName()) + .setRemark(param.getRemark()); + iotSpeakerDeviceManager.updateById(entity); + } + + /// 分页 + public PageResult page(PageParam pageParam, IotSpeakerDeviceQuery query) { + return MpUtil.toPageResult(iotSpeakerDeviceManager.page(pageParam, query)); + } + + /// 根据id查询 + public IotSpeakerDeviceResult findById(Long id) { + return iotSpeakerDeviceManager.findById(id) + // 云音响: 设备不存在 + .orElseThrow(() -> new DataNotExistException("error.iot.speaker.deviceNotFound")) + .toResult(); + } + + /// 删除 + public void delete(Long id) { + iotSpeakerDeviceManager.findById(id) + .orElseThrow(() -> new DataNotExistException("error.iot.speaker.deviceNotFound")); + iotSpeakerDeviceManager.deleteById(id); + } + + /// 绑定设备(首期仅更新本地状态为在线, 真实商米对接由独立服务完成) + @Transactional(rollbackFor = Exception.class) + public void bind(Long id) { + IotSpeakerDevice entity = iotSpeakerDeviceManager.findById(id) + .orElseThrow(() -> new DataNotExistException("error.iot.speaker.deviceNotFound")); + entity.setStatus(IotDeviceStatusEnum.ONLINE.getCode()) + .setBindTime(OffsetDateTime.now()); + iotSpeakerDeviceManager.updateById(entity); + } + + /// 解绑设备(首期仅更新本地状态为未绑定) + @Transactional(rollbackFor = Exception.class) + public void unbind(Long id) { + IotSpeakerDevice entity = iotSpeakerDeviceManager.findById(id) + .orElseThrow(() -> new DataNotExistException("error.iot.speaker.deviceNotFound")); + entity.setStatus(IotDeviceStatusEnum.UNBOUND.getCode()); + iotSpeakerDeviceManager.updateById(entity); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/dao/IotSpeakerDeviceManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/dao/IotSpeakerDeviceManager.java new file mode 100644 index 000000000..3bdcf5a23 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/dao/IotSpeakerDeviceManager.java @@ -0,0 +1,43 @@ +package cn.daxpay.open.payment.iot.speaker.dao; + +import cn.daxpay.open.payment.iot.speaker.entity.IotSpeakerDevice; +import cn.daxpay.open.payment.iot.speaker.param.IotSpeakerDeviceQuery; +import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager; +import cn.daxpay.open.platform.common.mybatisplus.query.generator.QueryGenerator; +import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/// # 云音响设备管理 +/// +@Slf4j +@Repository +@RequiredArgsConstructor +public class IotSpeakerDeviceManager extends BaseManager { + + /// 根据设备SN查询 + public Optional findByDeviceSn(String deviceSn) { + return findByField(IotSpeakerDevice::getDeviceSn, deviceSn); + } + + /// 判断设备SN是否存在(排除指定id, excludeId 为 null 时不排除) + public boolean existsByDeviceSn(String deviceSn, Long excludeId) { + if (excludeId == null) { + return existedByField(IotSpeakerDevice::getDeviceSn, deviceSn); + } + return existedByField(IotSpeakerDevice::getDeviceSn, deviceSn, excludeId); + } + + /// 分页 + public Page page(PageParam pageParam, IotSpeakerDeviceQuery query) { + Page mpPage = MpUtil.getMpPage(pageParam); + QueryWrapper wrapper = QueryGenerator.generator(query); + return this.page(mpPage, wrapper); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/dao/IotSpeakerDeviceMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/dao/IotSpeakerDeviceMapper.java new file mode 100644 index 000000000..524b40b89 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/dao/IotSpeakerDeviceMapper.java @@ -0,0 +1,10 @@ +package cn.daxpay.open.payment.iot.speaker.dao; + +import cn.daxpay.open.payment.iot.speaker.entity.IotSpeakerDevice; +import com.github.yulichang.base.MPJBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/// # 云音响设备 +@Mapper +public interface IotSpeakerDeviceMapper extends MPJBaseMapper { +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/entity/IotSpeakerDevice.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/entity/IotSpeakerDevice.java new file mode 100644 index 000000000..1dcec81af --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/entity/IotSpeakerDevice.java @@ -0,0 +1,68 @@ +package cn.daxpay.open.payment.iot.speaker.entity; + +import cn.daxpay.open.payment.iot.speaker.enums.IotDeviceStatusEnum; +import cn.daxpay.open.payment.iot.speaker.result.IotSpeakerDeviceResult; +import cn.daxpay.open.platform.common.mybatisplus.base.MpBaseEntity; +import cn.daxpay.open.platform.common.mybatisplus.function.ToResult; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.OffsetDateTime; + +/// # 云音响设备 +/// +/// 记录商米云音响设备与商户/门店的绑定关系, 真实播报对接由独立服务 dax-pay-iot 完成。 +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@TableName("iot_speaker_device") +public class IotSpeakerDevice extends MpBaseEntity implements ToResult { + + /// 商户号 + private String mchNo; + + /// 商米设备序列号(SN) + private String deviceSn; + + /// 设备IMEI + private String imei; + + /// 商米门店ID + private String shopId; + + /// 设备名称 + private String deviceName; + + /// 设备状态 + /// @see IotDeviceStatusEnum + private String status; + + /// 绑定时间 + private OffsetDateTime bindTime; + + /// 最后在线时间 + private OffsetDateTime lastOnlineTime; + + /// 备注 + private String remark; + + /// 转换为返回对象 + @Override + public IotSpeakerDeviceResult toResult() { + IotSpeakerDeviceResult result = new IotSpeakerDeviceResult() + .setMchNo(mchNo) + .setDeviceSn(deviceSn) + .setImei(imei) + .setShopId(shopId) + .setDeviceName(deviceName) + .setStatus(status) + .setBindTime(bindTime) + .setLastOnlineTime(lastOnlineTime) + .setRemark(remark); + result.setId(getId()); + result.setCreateTime(getCreateTime()); + return result; + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/enums/IotDeviceStatusEnum.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/enums/IotDeviceStatusEnum.java new file mode 100644 index 000000000..6c80fca5b --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/enums/IotDeviceStatusEnum.java @@ -0,0 +1,43 @@ +package cn.daxpay.open.payment.iot.speaker.enums; + +import cn.daxpay.open.platform.core.exception.DataNotExistException; +import cn.daxpay.open.platform.core.i18n.I18nSupport; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/// # 云音响设备状态 +/// +/// 字典: iot_speaker_device_status +@Getter +@RequiredArgsConstructor +public enum IotDeviceStatusEnum implements I18nSupport { + + /// 未绑定 + UNBOUND("unbound"), + /// 在线 + ONLINE("online"), + /// 离线 + OFFLINE("offline"), + /// 故障 + FAULT("fault"); + + /// 编码 + private final String code; + + /// 翻译 key 前缀 + @Override + public String getI18nPrefix() { + return "enum.iot_speaker_device_status"; + } + + /// 根据编码查找 + public static IotDeviceStatusEnum findByCode(String code) { + return Arrays.stream(values()) + .filter(e -> e.getCode().equals(code)) + .findFirst() + // 通用: 未找到对应的云音响设备状态: {0} + .orElseThrow(() -> new DataNotExistException("error.iot.speaker.deviceStatusNotFound", code)); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/param/IotSpeakerDeviceParam.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/param/IotSpeakerDeviceParam.java new file mode 100644 index 000000000..4cc881b94 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/param/IotSpeakerDeviceParam.java @@ -0,0 +1,46 @@ +package cn.daxpay.open.payment.iot.speaker.param; + +import cn.daxpay.open.platform.core.validation.ValidationGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 云音响设备 +@Data +@Accessors(chain = true) +@Schema(title = "云音响设备") +public class IotSpeakerDeviceParam { + + /// 主键 + @Schema(description = "主键") + @NotNull(message = "{validation.field.id.notNull}", groups = ValidationGroup.edit.class) + private Long id; + + /// 商户号 + @Schema(description = "商户号") + @NotBlank(message = "{validation.field.mchNo.notBlank}") + private String mchNo; + + /// 设备序列号 + @Schema(description = "设备序列号") + @NotBlank(message = "{validation.field.deviceSn.notBlank}") + private String deviceSn; + + /// 设备IMEI + @Schema(description = "设备IMEI") + private String imei; + + /// 商米门店ID + @Schema(description = "商米门店ID") + private String shopId; + + /// 设备名称 + @Schema(description = "设备名称") + private String deviceName; + + /// 备注 + @Schema(description = "备注") + private String remark; +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/param/IotSpeakerDeviceQuery.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/param/IotSpeakerDeviceQuery.java new file mode 100644 index 000000000..8c9d0a1db --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/param/IotSpeakerDeviceQuery.java @@ -0,0 +1,38 @@ +package cn.daxpay.open.payment.iot.speaker.param; + +import cn.daxpay.open.platform.core.annotation.QueryParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 云音响设备查询参数 +@Data +@QueryParam(type = QueryParam.CompareTypeEnum.LIKE) +@Accessors(chain = true) +@Schema(title = "云音响设备查询参数") +public class IotSpeakerDeviceQuery { + + /// 商户号 + @Schema(description = "商户号") + @QueryParam(type = QueryParam.CompareTypeEnum.EQ) + private String mchNo; + + /// 设备序列号 + @Schema(description = "设备序列号") + private String deviceSn; + + /// 设备名称 + @Schema(description = "设备名称") + private String deviceName; + + /// 商米门店ID + @Schema(description = "商米门店ID") + @QueryParam(type = QueryParam.CompareTypeEnum.EQ) + private String shopId; + + /// 设备状态 + /// @see cn.daxpay.open.payment.iot.speaker.enums.IotDeviceStatusEnum + @Schema(description = "设备状态") + @QueryParam(type = QueryParam.CompareTypeEnum.EQ) + private String status; +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/result/IotSpeakerDeviceResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/result/IotSpeakerDeviceResult.java new file mode 100644 index 000000000..19f46fec5 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/iot/speaker/result/IotSpeakerDeviceResult.java @@ -0,0 +1,47 @@ +package cn.daxpay.open.payment.iot.speaker.result; + +import cn.daxpay.open.payment.iot.speaker.enums.IotDeviceStatusEnum; +import cn.daxpay.open.platform.core.result.BaseResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.OffsetDateTime; + +/// # 云音响设备 +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "云音响设备") +public class IotSpeakerDeviceResult extends BaseResult { + + @Schema(description = "商户号") + private String mchNo; + + @Schema(description = "商米设备序列号(SN)") + private String deviceSn; + + @Schema(description = "设备IMEI") + private String imei; + + @Schema(description = "商米门店ID") + private String shopId; + + @Schema(description = "设备名称") + private String deviceName; + + /// 设备状态 + /// @see IotDeviceStatusEnum + @Schema(description = "设备状态(unbound未绑定/online在线/offline离线/fault故障)") + private String status; + + @Schema(description = "绑定时间") + private OffsetDateTime bindTime; + + @Schema(description = "最后在线时间") + private OffsetDateTime lastOnlineTime; + + @Schema(description = "备注") + private String remark; +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/iot_speaker_device_status.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/iot_speaker_device_status.json new file mode 100644 index 000000000..bd4fe0ad8 --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/iot_speaker_device_status.json @@ -0,0 +1,6 @@ +{ + "unbound": "Unbound", + "online": "Online", + "offline": "Offline", + "fault": "Fault" +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/iot/speaker.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/iot/speaker.json new file mode 100644 index 000000000..ce3de5b71 --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/iot/speaker.json @@ -0,0 +1,5 @@ +{ + "deviceNotFound": "Cloud speaker device not found", + "deviceSnExists": "Device serial number already exists", + "deviceStatusNotFound": "Cloud speaker device status not found: {0}" +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/iot_speaker_device_status.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/iot_speaker_device_status.json new file mode 100644 index 000000000..b7033aa44 --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/iot_speaker_device_status.json @@ -0,0 +1,6 @@ +{ + "unbound": "未绑定", + "online": "在线", + "offline": "离线", + "fault": "故障" +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/iot/speaker.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/iot/speaker.json new file mode 100644 index 000000000..89874103f --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/iot/speaker.json @@ -0,0 +1,5 @@ +{ + "deviceNotFound": "云音响设备不存在", + "deviceSnExists": "设备序列号已存在", + "deviceStatusNotFound": "未找到对应的云音响设备状态: {0}" +}