419 lines
18 KiB
Java
419 lines
18 KiB
Java
package io.as.modules.openapi.controller;
|
||||
|
|
|
|||
|
|
import io.as.common.annotation.AuditAction;
|
|||
|
|
import io.as.common.exception.ASException;
|
|||
|
|
import io.as.modules.daycard.constants.DayCardConstants;
|
|||
|
|
import io.as.modules.daycard.entity.DayCardActivationCodeEntity;
|
|||
|
|
import io.as.modules.daycard.entity.DayCardOrderEntity;
|
|||
|
|
import io.as.modules.daycard.entity.DayCardPoolEntity;
|
|||
|
|
import io.as.modules.daycard.entity.DayCardPriceConfigEntity;
|
|||
|
|
import io.as.modules.daycard.service.DayCardActivationCodeService;
|
|||
|
|
import io.as.modules.daycard.service.DayCardOrderService;
|
|||
|
|
import io.as.modules.device.entity.DeviceEntity;
|
|||
|
|
import io.as.modules.device.service.DeviceService;
|
|||
|
|
import io.as.modules.openapi.filter.ApiDeviceSignFilter;
|
|||
|
|
import io.as.modules.openapi.form.DeviceInitRequest;
|
|||
|
|
import io.as.modules.openapi.sign.ApiDeviceSignService;
|
|||
|
|
import io.as.modules.openapi.sign.DeviceSignResult;
|
|||
|
|
import io.as.modules.sys.entity.SysOrgEntity;
|
|||
|
|
import io.as.modules.sys.service.SysOrgService;
|
|||
|
|
import io.swagger.annotations.Api;
|
|||
|
|
import io.swagger.annotations.ApiImplicitParam;
|
|||
|
|
import io.swagger.annotations.ApiImplicitParams;
|
|||
|
|
import io.swagger.annotations.ApiOperation;
|
|||
|
|
import io.swagger.annotations.ApiParam;
|
|||
|
|
import org.apache.commons.lang.StringUtils;
|
|||
|
|
import org.slf4j.Logger;
|
|||
|
|
import org.slf4j.LoggerFactory;
|
|||
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|||
|
|
import org.springframework.web.bind.annotation.*;
|
|||
|
|
|
|||
|
|
import javax.servlet.http.HttpServletRequest;
|
|||
|
|
import java.util.Date;
|
|||
|
|
import java.util.HashMap;
|
|||
|
|
import java.util.Map;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 橘视听 API 设备接口(对齐生产环境 as-partner-backend 的 ApiJstController)
|
|||
|
|
* <p>完整流程:</p>
|
|||
|
|
* <ol>
|
|||
|
|
* <li><b>init</b>:设备初始化(注册/更新),返回 sign</li>
|
|||
|
|
* <li><b>checkBenefits</b>:查询天卡权益,返回 thirdCardNo 与 status(ACTIVE/UNBOUND)</li>
|
|||
|
|
* <li>无权益 → <b>daycard/create</b> 创建天卡订单 → 支付 → 轮询 checkBenefits 拿 thirdCardNo</li>
|
|||
|
|
* </ol>
|
|||
|
|
* <p><b>鉴权</b>:除 POST /api/jst/init 外,均须在请求头携带 sign。缺少或无效 sign 由过滤器直接返回 HTTP 401。</p>
|
|||
|
|
*/
|
|||
|
|
@Api(tags = "橘视听-API-设备接口",
|
|||
|
|
description = "除 POST /api/jst/init 外均需请求头 sign;缺/无效 sign→401。业务结果以 JSON 的 code 为准。")
|
|||
|
|
@RestController
|
|||
|
|
@RequestMapping("/api/jst")
|
|||
|
|
public class ApiJstController {
|
|||
|
|
|
|||
|
|
private static final Logger logger = LoggerFactory.getLogger(ApiJstController.class);
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private ApiDeviceSignService apiDeviceSignService;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private DeviceService deviceService;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private SysOrgService sysOrgService;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private DayCardOrderService dayCardOrderService;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private DayCardActivationCodeService dayCardActivationCodeService;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设备初始化:校验设备是否在系统,不在则添加、在则更新,返回 sign
|
|||
|
|
*/
|
|||
|
|
@ApiOperation(value = "设备初始化",
|
|||
|
|
notes = "本接口无需 sign。body 必传 activationCode(管理后台按门店生成的激活码);设备可传 mac、wifiMac、deviceSn、deviceUid 等。成功返回 data.device、sign。")
|
|||
|
|
@AuditAction(value = "DEVICE_INIT", description = "设备初始化")
|
|||
|
|
@PostMapping("/init")
|
|||
|
|
public Map<String, Object> init(
|
|||
|
|
@ApiParam(value = "设备初始化请求体", required = true)
|
|||
|
|
@RequestBody DeviceInitRequest request) {
|
|||
|
|
try {
|
|||
|
|
return doInit(request);
|
|||
|
|
} catch (ASException e) {
|
|||
|
|
return rrExceptionResponse(e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设备权益状态:查询天卡权益,返回 thirdCardNo + status
|
|||
|
|
* <p>status: ACTIVE(橘视厅激活成功且未过期,返回 thirdCardNo)/ ACTIVATING(激活中)/ ACTIVATION_FAILED(激活失败)/ UNBOUND(无天卡)</p>
|
|||
|
|
*/
|
|||
|
|
@ApiOperation(value = "设备权益状态",
|
|||
|
|
notes = "请求头必传 sign。查询设备天卡权益,返回 data.status(ACTIVE/ACTIVATING/ACTIVATION_FAILED/UNBOUND)。仅 ACTIVE 时返回 data.thirdCardNo(播放凭证)和 data.expireAt。")
|
|||
|
|
@ApiImplicitParams({
|
|||
|
|
@ApiImplicitParam(name = "sign", value = "设备签名(用于标识设备)", required = true, paramType = "header", dataType = "string")
|
|||
|
|
})
|
|||
|
|
@PostMapping("/checkBenefits")
|
|||
|
|
public Map<String, Object> checkBenefits(HttpServletRequest request) {
|
|||
|
|
Long deviceId = deviceIdFrom(request);
|
|||
|
|
DayCardPoolEntity pool = dayCardOrderService.getDeviceBenefits(deviceId);
|
|||
|
|
Map<String, Object> data = new HashMap<>();
|
|||
|
|
if (pool == null) {
|
|||
|
|
data.put("status", "UNBOUND");
|
|||
|
|
} else if (DayCardConstants.ACTIVATE_STATUS_SUCCESS.equals(pool.getActivateStatus())) {
|
|||
|
|
data.put("status", "ACTIVE");
|
|||
|
|
data.put("thirdCardNo", pool.getThirdCardNo());
|
|||
|
|
data.put("cardNo", pool.getCardCode());
|
|||
|
|
data.put("expireAt", pool.getExpireAt());
|
|||
|
|
} else if (DayCardConstants.ACTIVATE_STATUS_FAILED.equals(pool.getActivateStatus())) {
|
|||
|
|
data.put("status", "ACTIVATION_FAILED");
|
|||
|
|
data.put("activateError", pool.getActivateError());
|
|||
|
|
} else {
|
|||
|
|
// PENDING 或 null,激活中
|
|||
|
|
data.put("status", "ACTIVATING");
|
|||
|
|
}
|
|||
|
|
return buildSuccessResponse(data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设备天卡价格:供前端二维码展示
|
|||
|
|
* <p>按 3 层维度匹配当前设备生效价:门店配置价 > 渠道配置价 > 平台默认价</p>
|
|||
|
|
*/
|
|||
|
|
@ApiOperation(value = "设备天卡价格",
|
|||
|
|
notes = "请求头必传 sign。返回 data.price(元)、data.priceFen(分)、data.name。未配置定价时 data.status=UNCONFIGURED。")
|
|||
|
|
@ApiImplicitParams({
|
|||
|
|
@ApiImplicitParam(name = "sign", value = "设备签名", required = true, paramType = "header", dataType = "string")
|
|||
|
|
})
|
|||
|
|
@PostMapping("/price")
|
|||
|
|
public Map<String, Object> price(HttpServletRequest request) {
|
|||
|
|
Long deviceId = deviceIdFrom(request);
|
|||
|
|
DayCardPriceConfigEntity config = dayCardOrderService.getDevicePrice(deviceId);
|
|||
|
|
Map<String, Object> data = new HashMap<>();
|
|||
|
|
if (config == null) {
|
|||
|
|
data.put("status", "UNCONFIGURED");
|
|||
|
|
} else {
|
|||
|
|
data.put("status", "OK");
|
|||
|
|
data.put("price", config.getPrice());
|
|||
|
|
data.put("priceFen", config.getPrice().multiply(new java.math.BigDecimal("100")).intValue());
|
|||
|
|
data.put("name", config.getName());
|
|||
|
|
}
|
|||
|
|
return buildSuccessResponse(data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建天卡订单(无权益时调用)
|
|||
|
|
*/
|
|||
|
|
@ApiOperation(value = "创建天卡订单",
|
|||
|
|
notes = "请求头必传 sign。从 sign 解析 deviceId,无需传设备标识。返回 data.orderNo、data.status、data.amount(分)、data.codeUrl、data.expireAt。")
|
|||
|
|
@AuditAction(value = "DAYCARD_ORDER_CREATE", description = "创建天卡订单(设备端)")
|
|||
|
|
@ApiImplicitParams({
|
|||
|
|
@ApiImplicitParam(name = "sign", value = "设备签名", required = true, paramType = "header", dataType = "string")
|
|||
|
|
})
|
|||
|
|
@PostMapping("/daycard/create")
|
|||
|
|
public Map<String, Object> createDayCard(HttpServletRequest request) {
|
|||
|
|
try {
|
|||
|
|
Long deviceId = deviceIdFrom(request);
|
|||
|
|
DayCardOrderEntity order = dayCardOrderService.createOrder(deviceId);
|
|||
|
|
Map<String, Object> data = new HashMap<>();
|
|||
|
|
data.put("orderNo", order.getOrderNo());
|
|||
|
|
data.put("status", order.getStatus());
|
|||
|
|
data.put("amount", order.getAmount().multiply(new java.math.BigDecimal("100")).intValue());
|
|||
|
|
data.put("cardNo", order.getCardNo());
|
|||
|
|
data.put("codeUrl", order.getCodeUrl());
|
|||
|
|
data.put("expireAt", order.getExpireTime());
|
|||
|
|
data.put("deviceId", order.getDeviceId());
|
|||
|
|
return buildSuccessResponse(data);
|
|||
|
|
} catch (ASException e) {
|
|||
|
|
return rrExceptionResponse(e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 查询支付状态
|
|||
|
|
*/
|
|||
|
|
@ApiOperation(value = "查询支付状态",
|
|||
|
|
notes = "请求头必传 sign。路径参数为订单号,返回 data.status(CREATED/PAYING/PAID/CANCELLED)、data.codeUrl 等。")
|
|||
|
|
@ApiImplicitParams({
|
|||
|
|
@ApiImplicitParam(name = "sign", value = "设备签名", required = true, paramType = "header", dataType = "string")
|
|||
|
|
})
|
|||
|
|
@GetMapping("/daycard/status/{orderNo}")
|
|||
|
|
public Map<String, Object> daycardStatus(@PathVariable("orderNo") String orderNo) {
|
|||
|
|
DayCardOrderEntity order = dayCardOrderService.getByOrderNo(orderNo);
|
|||
|
|
if (order == null) {
|
|||
|
|
Map<String, Object> err = new HashMap<>();
|
|||
|
|
err.put("code", 404);
|
|||
|
|
err.put("msg", "订单不存在");
|
|||
|
|
err.put("data", new HashMap<>());
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
Map<String, Object> data = new HashMap<>();
|
|||
|
|
data.put("orderNo", order.getOrderNo());
|
|||
|
|
data.put("status", order.getStatus());
|
|||
|
|
data.put("paidAt", order.getPayTime());
|
|||
|
|
data.put("codeUrl", order.getCodeUrl());
|
|||
|
|
data.put("amount", order.getAmount() != null ? order.getAmount().multiply(new java.math.BigDecimal("100")).intValue() : 0);
|
|||
|
|
data.put("payChannel", order.getPayChannel());
|
|||
|
|
return buildSuccessResponse(data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设备心跳:更新设备心跳时间,续期 sign
|
|||
|
|
*/
|
|||
|
|
@ApiOperation(value = "设备心跳",
|
|||
|
|
notes = "请求头必传 sign。更新设备 last_heartbeat 并续期 sign 缓存 TTL。建议按业务间隔调用以防 sign 过期。")
|
|||
|
|
@ApiImplicitParams({
|
|||
|
|
@ApiImplicitParam(name = "sign", value = "设备签名", required = true, paramType = "header", dataType = "string")
|
|||
|
|
})
|
|||
|
|
@PostMapping("/heartBeat")
|
|||
|
|
public Map<String, Object> heartBeat(HttpServletRequest request) {
|
|||
|
|
Long deviceId = deviceIdFrom(request);
|
|||
|
|
DeviceEntity device = deviceService.getById(deviceId);
|
|||
|
|
if (device != null) {
|
|||
|
|
device.setLastHeartbeat(new Date());
|
|||
|
|
deviceService.updateDevice(device);
|
|||
|
|
}
|
|||
|
|
apiDeviceSignService.refreshExpire(deviceId);
|
|||
|
|
return buildSuccessResponse(new HashMap<>());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 刷新 sign:换新 sign,旧 sign 立即失效
|
|||
|
|
*/
|
|||
|
|
@ApiOperation(value = "刷新 sign",
|
|||
|
|
notes = "请求头必传当前有效 sign。成功后在 data 中返回新 sign;旧 sign 立即失效,客户端必须替换本地存储。")
|
|||
|
|
@ApiImplicitParams({
|
|||
|
|
@ApiImplicitParam(name = "sign", value = "设备签名(旧的即将过期 sign)", required = true, paramType = "header", dataType = "string")
|
|||
|
|
})
|
|||
|
|
@PostMapping("/refreshSign")
|
|||
|
|
public Map<String, Object> refreshSign(HttpServletRequest request) {
|
|||
|
|
Long deviceId = deviceIdFrom(request);
|
|||
|
|
String orgCode = channelOrgCodeFrom(request);
|
|||
|
|
String newSign = apiDeviceSignService.generate(deviceId, System.currentTimeMillis(), orgCode);
|
|||
|
|
Map<String, Object> data = new HashMap<>();
|
|||
|
|
data.put("sign", newSign);
|
|||
|
|
return buildSuccessResponse(data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 内部方法 ====================
|
|||
|
|
|
|||
|
|
private Map<String, Object> doInit(DeviceInitRequest request) {
|
|||
|
|
if (request == null) {
|
|||
|
|
throw new ASException("请求体不能为空", 400);
|
|||
|
|
}
|
|||
|
|
String activationCode = trim(request.getActivationCode());
|
|||
|
|
if (StringUtils.isBlank(activationCode)) {
|
|||
|
|
// 代销模式:免activationCode,按设备标识查找已注册设备(由partner消耗天卡接口创建)
|
|||
|
|
return doInitWithoutActivationCode(request);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 通过激活码解析门店与渠道
|
|||
|
|
DayCardActivationCodeEntity codeEntity = dayCardActivationCodeService.getByCode(activationCode);
|
|||
|
|
if (codeEntity == null) {
|
|||
|
|
throw new ASException("激活码无效或已禁用", 400);
|
|||
|
|
}
|
|||
|
|
Long storeOrgId = codeEntity.getStoreOrgId();
|
|||
|
|
Long channelOrgId = codeEntity.getChannelOrgId();
|
|||
|
|
if (storeOrgId == null) {
|
|||
|
|
throw new ASException("激活码未绑定门店", 400);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 校验门店存在
|
|||
|
|
SysOrgEntity storeOrg = sysOrgService.getById(storeOrgId);
|
|||
|
|
if (storeOrg == null) {
|
|||
|
|
throw new ASException("门店不存在", 400);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 校验设备数上限
|
|||
|
|
if (codeEntity.getMaxDeviceCount() != null && codeEntity.getMaxDeviceCount() > 0) {
|
|||
|
|
int activated = codeEntity.getActivatedCount() != null ? codeEntity.getActivatedCount() : 0;
|
|||
|
|
if (activated >= codeEntity.getMaxDeviceCount()) {
|
|||
|
|
throw new ASException("该激活码已达设备上限(" + codeEntity.getMaxDeviceCount() + "台)", 400);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
String androidId = trim(request.getAndroidId());
|
|||
|
|
String mac = trim(request.getMac());
|
|||
|
|
String wifiMac = trim(request.getWifiMac());
|
|||
|
|
String deviceSn = trim(request.getDeviceSn());
|
|||
|
|
String deviceUid = trim(request.getDeviceUid());
|
|||
|
|
|
|||
|
|
// 查找或创建设备(按优先级降权匹配:androidId > mac > deviceSn > wifiMac > deviceUid)
|
|||
|
|
DeviceEntity device = findOrCreateDevice(androidId, mac, wifiMac, deviceSn, deviceUid);
|
|||
|
|
boolean isNewDevice = device.getId() == null;
|
|||
|
|
Date now = new Date();
|
|||
|
|
device.setStoreOrgId(storeOrgId);
|
|||
|
|
// 仅用非空标识更新,避免覆盖已有标识(按文档"更新缺失的标识字段")
|
|||
|
|
if (StringUtils.isNotBlank(androidId)) device.setAndroidId(androidId);
|
|||
|
|
if (StringUtils.isNotBlank(mac)) device.setMac(mac);
|
|||
|
|
if (StringUtils.isNotBlank(wifiMac)) device.setWifiMac(wifiMac);
|
|||
|
|
if (StringUtils.isNotBlank(deviceSn)) device.setDeviceSn(deviceSn);
|
|||
|
|
if (StringUtils.isNotBlank(deviceUid)) device.setDeviceUid(deviceUid);
|
|||
|
|
if (StringUtils.isNotBlank(request.getManufacturer())) device.setManufacturer(trim(request.getManufacturer()));
|
|||
|
|
if (StringUtils.isNotBlank(request.getModel())) device.setModel(trim(request.getModel()));
|
|||
|
|
if (StringUtils.isNotBlank(request.getBrand())) device.setBrand(trim(request.getBrand()));
|
|||
|
|
if (StringUtils.isNotBlank(request.getVerCode())) device.setVerCode(trim(request.getVerCode()));
|
|||
|
|
if (StringUtils.isNotBlank(request.getVerName())) device.setVerName(trim(request.getVerName()));
|
|||
|
|
if (StringUtils.isNotBlank(request.getAndroidVersion())) device.setAndroidVersion(trim(request.getAndroidVersion()));
|
|||
|
|
if (StringUtils.isNotBlank(request.getDeviceModel())) device.setDeviceModel(trim(request.getDeviceModel()));
|
|||
|
|
if (StringUtils.isNotBlank(request.getDeviceName())) device.setDeviceName(trim(request.getDeviceName()));
|
|||
|
|
device.setLastHeartbeat(now);
|
|||
|
|
if (isNewDevice) {
|
|||
|
|
device.setCreatedAt(now);
|
|||
|
|
device.setDeleteFlag("UNDEL");
|
|||
|
|
device.setStatus("ACTIVE");
|
|||
|
|
deviceService.saveDevice(device);
|
|||
|
|
// 新设备激活计数 +1
|
|||
|
|
dayCardActivationCodeService.incrementActivatedCount(codeEntity.getId());
|
|||
|
|
} else {
|
|||
|
|
device.setUpdatedAt(now);
|
|||
|
|
deviceService.updateDevice(device);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成 sign(orgCode 用渠道组织ID的字符串形式,sign 校验时不依赖此值)
|
|||
|
|
String orgCode = channelOrgId != null ? String.valueOf(channelOrgId) : "";
|
|||
|
|
String sign = apiDeviceSignService.generate(device.getId(), System.currentTimeMillis(), orgCode);
|
|||
|
|
|
|||
|
|
Map<String, Object> data = new HashMap<>();
|
|||
|
|
Map<String, Object> deviceInfo = new HashMap<>();
|
|||
|
|
deviceInfo.put("deviceId", device.getId());
|
|||
|
|
deviceInfo.put("roomNo", device.getRoomNo());
|
|||
|
|
data.put("device", deviceInfo);
|
|||
|
|
data.put("sign", sign);
|
|||
|
|
return buildSuccessResponse(data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 代销模式免activationCode初始化:按设备标识查找已注册设备,返回sign
|
|||
|
|
* <p>设备由 /openapi/partner/card/consume 接口创建,APK用同一设备标识调本方法拿sign</p>
|
|||
|
|
*/
|
|||
|
|
private Map<String, Object> doInitWithoutActivationCode(DeviceInitRequest request) {
|
|||
|
|
String mac = trim(request.getMac());
|
|||
|
|
String wifiMac = trim(request.getWifiMac());
|
|||
|
|
String deviceSn = trim(request.getDeviceSn());
|
|||
|
|
String deviceUid = trim(request.getDeviceUid());
|
|||
|
|
if (StringUtils.isBlank(mac) && StringUtils.isBlank(wifiMac)
|
|||
|
|
&& StringUtils.isBlank(deviceSn) && StringUtils.isBlank(deviceUid)) {
|
|||
|
|
throw new ASException("代销模式免激活码,需至少传一个设备标识(mac/wifiMac/deviceSn/deviceUid)", 400);
|
|||
|
|
}
|
|||
|
|
// 按优先级查找已有设备:mac > wifiMac > deviceSn > deviceUid(与生产环境一致)
|
|||
|
|
DeviceEntity device = findByIdentifier(mac, DeviceEntity::getMac);
|
|||
|
|
if (device == null) device = findByIdentifier(wifiMac, DeviceEntity::getWifiMac);
|
|||
|
|
if (device == null) device = findByIdentifier(deviceSn, DeviceEntity::getDeviceSn);
|
|||
|
|
if (device == null) device = findByIdentifier(deviceUid, DeviceEntity::getDeviceUid);
|
|||
|
|
if (device == null) {
|
|||
|
|
throw new ASException("设备未注册,请先通过合作伙伴天卡消耗接口激活设备", 400);
|
|||
|
|
}
|
|||
|
|
// 更新心跳
|
|||
|
|
device.setLastHeartbeat(new Date());
|
|||
|
|
deviceService.updateDevice(device);
|
|||
|
|
// 生成sign(orgCode留空,代销模式不依赖渠道组织)
|
|||
|
|
String sign = apiDeviceSignService.generate(device.getId(), System.currentTimeMillis(), "");
|
|||
|
|
Map<String, Object> data = new HashMap<>();
|
|||
|
|
Map<String, Object> deviceInfo = new HashMap<>();
|
|||
|
|
deviceInfo.put("deviceId", device.getId());
|
|||
|
|
deviceInfo.put("roomNo", device.getRoomNo());
|
|||
|
|
data.put("device", deviceInfo);
|
|||
|
|
Map<String, Object> result = buildSuccessResponse(data);
|
|||
|
|
result.put("sign", sign);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 查找或创建设备:按 mac 优先查找,无则新建
|
|||
|
|
* <p>注:此处为简化实现,完整的多策略识别算法在 DeviceResolveService 中</p>
|
|||
|
|
*/
|
|||
|
|
private DeviceEntity findOrCreateDevice(String androidId, String mac, String wifiMac, String deviceSn, String deviceUid) {
|
|||
|
|
if (StringUtils.isBlank(androidId) && StringUtils.isBlank(mac)
|
|||
|
|
&& StringUtils.isBlank(wifiMac) && StringUtils.isBlank(deviceSn)
|
|||
|
|
&& StringUtils.isBlank(deviceUid)) {
|
|||
|
|
throw new ASException("androidId、mac、wifiMac、deviceSn、deviceUid 至少传一个", 400);
|
|||
|
|
}
|
|||
|
|
// 按优先级降权匹配:androidId > mac > deviceSn > wifiMac > deviceUid
|
|||
|
|
DeviceEntity existing = findByIdentifier(androidId, DeviceEntity::getAndroidId);
|
|||
|
|
if (existing == null) existing = findByIdentifier(mac, DeviceEntity::getMac);
|
|||
|
|
if (existing == null) existing = findByIdentifier(deviceSn, DeviceEntity::getDeviceSn);
|
|||
|
|
if (existing == null) existing = findByIdentifier(wifiMac, DeviceEntity::getWifiMac);
|
|||
|
|
if (existing == null) existing = findByIdentifier(deviceUid, DeviceEntity::getDeviceUid);
|
|||
|
|
return existing != null ? existing : new DeviceEntity();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private DeviceEntity findByIdentifier(String value, com.baomidou.mybatisplus.core.toolkit.support.SFunction<DeviceEntity, ?> column) {
|
|||
|
|
if (StringUtils.isBlank(value)) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
return deviceService.lambdaQuery()
|
|||
|
|
.eq(column, value)
|
|||
|
|
.eq(DeviceEntity::getDeleteFlag, "UNDEL")
|
|||
|
|
.last("LIMIT 1").one();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static Long deviceIdFrom(HttpServletRequest request) {
|
|||
|
|
return (Long) request.getAttribute(ApiDeviceSignFilter.REQUEST_ATTR_DEVICE_ID);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static String channelOrgCodeFrom(HttpServletRequest request) {
|
|||
|
|
DeviceSignResult signResult =
|
|||
|
|
(DeviceSignResult) request.getAttribute(ApiDeviceSignFilter.REQUEST_ATTR_DEVICE_SIGN_RESULT);
|
|||
|
|
return signResult != null ? signResult.getOrgCode() : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static String trim(String s) {
|
|||
|
|
return s == null ? null : s.trim();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static Map<String, Object> buildSuccessResponse(Map<String, Object> data) {
|
|||
|
|
Map<String, Object> result = new HashMap<>();
|
|||
|
|
result.put("code", 0);
|
|||
|
|
result.put("msg", "success");
|
|||
|
|
result.put("data", data != null ? data : new HashMap<>());
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static Map<String, Object> rrExceptionResponse(ASException e) {
|
|||
|
|
Map<String, Object> err = new HashMap<>();
|
|||
|
|
err.put("code", e.getCode());
|
|||
|
|
err.put("msg", e.getMessage());
|
|||
|
|
err.put("data", new HashMap<>());
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
}
|