diff --git a/mhd_thrid_party/src/main/java/com/linke/thirdParty/application/server/NanqiGpsApplicationService.java b/mhd_thrid_party/src/main/java/com/linke/thirdParty/application/server/NanqiGpsApplicationService.java new file mode 100644 index 000000000..3d5aa0741 --- /dev/null +++ b/mhd_thrid_party/src/main/java/com/linke/thirdParty/application/server/NanqiGpsApplicationService.java @@ -0,0 +1,214 @@ +package com.linke.thirdParty.application.server; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.linke.thirdParty.domain.departInterfaceInfo.repository.todo.SysDepartInterfaceInfoDo; +import com.linke.thirdParty.infrastructure.dict.NanqiGpsConstant; +import com.linke.thirdParty.interfaces.vo.gps.NanqiGpsLocationVO; +import com.mhd.common.core.domain.po.thirdparty.SysDepartInterfaceInfoPo; +import com.mhd.common.core.exception.ServiceException; +import com.mhd.common.core.utils.HttpClientUtil; +import com.mhd.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.DigestUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 南岐车联网GPS平台 对接应用服务 + * + *

提供两个接口:

+ *
    + *
  1. login:登录获取 sessionId,并缓存到 Redis
  2. + *
  3. queryByPlate:根据车牌号+sessionId 查询车辆实时定位
  4. + *
+ * + * @author mhd + */ +@Slf4j +@Service +public class NanqiGpsApplicationService { + + @Autowired + private SysDepartInterfaceInfoApplicationService interfaceInfoApplicationService; + + @Autowired + private RedisService redisService; + + /** + * 南岐登录账号 + */ + private String userId = NanqiGpsConstant.DEFAULT_USER_ID; + + /** + * 南岐登录密码(明文) + */ + private String password = NanqiGpsConstant.DEFAULT_PASSWORD; + + /** + * 从三方接口配置表读取南岐配置(interfaceType = nanqi_gps),未配置则使用默认值 + */ + private void getBaseInfo() { + SysDepartInterfaceInfoDo sysDepartInterfaceInfoDo = new SysDepartInterfaceInfoDo(); + sysDepartInterfaceInfoDo.setInterfaceType(NanqiGpsConstant.INTERFACE_TYPE); + List sysDepartInterfaceInfoPos = interfaceInfoApplicationService.queryList1(sysDepartInterfaceInfoDo); + if (sysDepartInterfaceInfoPos == null || sysDepartInterfaceInfoPos.isEmpty()) { + return; + } + for (SysDepartInterfaceInfoPo info : sysDepartInterfaceInfoPos) { + String key = info.getInterfaceTypeKey(); + String value = info.getInterfaceTypeValue(); + if ("userId".equals(key)) { + userId = value; + } else if ("password".equals(key)) { + password = value; + } + } + } + + /** + * 登录获取 sessionId(若 Redis 已有有效 sessionId 则直接复用) + * + * @return sessionId + */ + public String login() { + return getSessionId(); + } + + /** + * 获取有效 sessionId:优先从 Redis 取,取不到则调用登录接口重新获取 + * + * @return sessionId + */ + public String getSessionId() { + String cacheKey = NanqiGpsConstant.SESSION_ID_KEY; + String sessionId = redisService.getCacheObject(cacheKey); + if (sessionId != null && !sessionId.isEmpty()) { + return sessionId; + } + // 重新登录 + getBaseInfo(); + sessionId = doLogin(userId, password, NanqiGpsConstant.LOGIN_TYPE_USER); + if (sessionId != null && !sessionId.isEmpty()) { + redisService.setCacheObject(cacheKey, sessionId, NanqiGpsConstant.SESSION_ID_EXPIRE_SECONDS, TimeUnit.SECONDS); + } + return sessionId; + } + + /** + * 手动刷新 sessionId(强制重新登录,忽略缓存) + * + * @return sessionId + */ + public String refreshSessionId() { + redisService.deleteObject(NanqiGpsConstant.SESSION_ID_KEY); + return getSessionId(); + } + + /** + * 执行南岐登录接口 + * + * @param loginUserId 登录账号 + * @param loginPassword 登录密码(明文) + * @param loginType user-用户 car-车辆/人员 + * @return sessionId + */ + private String doLogin(String loginUserId, String loginPassword, String loginType) { + Map params = new HashMap<>(); + params.put("userId", loginUserId); + // 密码需要 MD5 32 位小写加密 + String md5Pwd = DigestUtils.md5DigestAsHex(loginPassword.getBytes()).toLowerCase(); + params.put("password", md5Pwd); + params.put("loginType", loginType == null ? NanqiGpsConstant.LOGIN_TYPE_USER : loginType); + params.put("loginWay", NanqiGpsConstant.DEFAULT_LOGIN_WAY); + params.put("loginLang", NanqiGpsConstant.DEFAULT_LOGIN_LANG); + + String result = HttpClientUtil.doGet(NanqiGpsConstant.LOGIN_URL, params); + log.info("南岐GPS登录返回: {}", result); + JSONObject json = JSON.parseObject(result); + if (json == null) { + throw new ServiceException("南岐GPS登录接口无响应"); + } + Integer rspCode = json.getInteger("rspCode"); + if (rspCode == null || rspCode != 1) { + throw new ServiceException("南岐GPS登录失败:" + json.getString("rspDesc")); + } + return json.getString("sessionId"); + } + + /** + * 根据车牌号查询车辆实时定位 + * + * @param carPlate 车牌号 + * @return 车辆定位信息 + */ + public NanqiGpsLocationVO queryByPlate(String carPlate) { + if (carPlate == null || carPlate.trim().isEmpty()) { + throw new ServiceException("车牌号不能为空"); + } + String sessionId = getSessionId(); + if (sessionId == null || sessionId.isEmpty()) { + throw new ServiceException("获取南岐GPS sessionId失败"); + } + + Map params = new HashMap<>(); + params.put("carPlate", carPlate.trim()); + params.put("sessionId", sessionId); + + String result = HttpClientUtil.doGet(NanqiGpsConstant.GET_GPS_URL, params); + log.info("南岐GPS查询车辆[{}]返回: {}", carPlate, result); + JSONObject json = JSON.parseObject(result); + if (json == null) { + throw new ServiceException("南岐GPS查询接口无响应"); + } + Integer rspCode = json.getInteger("rspCode"); + if (rspCode == null || rspCode != 1) { + // 可能是 sessionId 失效,尝试刷新后重试一次 + String newSessionId = refreshSessionId(); + params.put("sessionId", newSessionId); + result = HttpClientUtil.doGet(NanqiGpsConstant.GET_GPS_URL, params); + json = JSON.parseObject(result); + if (json == null || !Integer.valueOf(1).equals(json.getInteger("rspCode"))) { + throw new ServiceException("南岐GPS查询失败:" + (json == null ? "" : json.getString("rspDesc"))); + } + } + + NanqiGpsLocationVO vo = new NanqiGpsLocationVO(); + vo.setRspCode(json.getInteger("rspCode")); + vo.setRspDesc(json.getString("rspDesc")); + + JSONArray list = json.getJSONArray("list"); + if (list != null && !list.isEmpty()) { + JSONObject loc = list.getJSONObject(0); + vo.setCarId(loc.getString("carId")); + vo.setCarPlate(loc.getString("carPlate")); + vo.setCarName(loc.getString("carName")); + vo.setTeamId(loc.getString("teamId")); + vo.setTeamName(loc.getString("teamName")); + vo.setTime(loc.getString("time")); + vo.setLng(loc.getBigDecimal("lng")); + vo.setLat(loc.getBigDecimal("lat")); + vo.setSpeed(loc.getBigDecimal("speed")); + vo.setDrct(loc.getString("drct")); + vo.setMile(loc.getString("mile")); + vo.setPreMile(loc.getString("preMile")); + vo.setSatl(loc.getString("satl")); + vo.setSgn(loc.getString("sgn")); + vo.setAddr(loc.getString("addr")); + vo.setState(loc.getString("state")); + vo.setStateCn(loc.getString("stateCn")); + vo.setDrvName(loc.getString("drvName")); + vo.setDrvPhone(loc.getString("drvPhone")); + vo.setCarType(loc.getString("carType")); + vo.setExpState(loc.getString("expState")); + vo.setExpTime(loc.getString("expTime")); + } + return vo; + } +} diff --git a/mhd_thrid_party/src/main/java/com/linke/thirdParty/infrastructure/dict/NanqiGpsConstant.java b/mhd_thrid_party/src/main/java/com/linke/thirdParty/infrastructure/dict/NanqiGpsConstant.java new file mode 100644 index 000000000..87d43c1c0 --- /dev/null +++ b/mhd_thrid_party/src/main/java/com/linke/thirdParty/infrastructure/dict/NanqiGpsConstant.java @@ -0,0 +1,64 @@ +package com.linke.thirdParty.infrastructure.dict; + +/** + * 南岐车联网GPS平台 接口常量 + * + * @author mhd + */ +public class NanqiGpsConstant { + + /** + * 三方接口配置表 interfaceType(对应 sys_depart_interface_info 表) + */ + public static final String INTERFACE_TYPE = "nanqi_gps"; + + /** + * 登录接口地址(第一接口) + */ + public static final String LOGIN_URL = "http://iov.yotugo.com/gps-web/api/login.jsp"; + + /** + * 车辆定位查询接口地址(第二接口) + */ + public static final String GET_GPS_URL = "http://iov.yotugo.com/gps-web/api/get_gps_r_plate.jsp"; + + /** + * 登录账号(默认值,可被 sys_depart_interface_info 配置覆盖,key 为 userId) + */ + public static final String DEFAULT_USER_ID = "zhnanqi"; + + /** + * 登录密码(默认值,明文,可被配置覆盖,key 为 password) + */ + public static final String DEFAULT_PASSWORD = "nq123456"; + + /** + * 登录方式:默认 interface + */ + public static final String DEFAULT_LOGIN_WAY = "interface"; + + /** + * 登录语言:默认中文 + */ + public static final String DEFAULT_LOGIN_LANG = "zh_CN"; + + /** + * 登录类型:car 表示按车辆/人员登录 + */ + public static final String LOGIN_TYPE_CAR = "car"; + + /** + * 登录类型:user 表示按用户登录 + */ + public static final String LOGIN_TYPE_USER = "user"; + + /** + * sessionId 缓存 key 前缀 + */ + public static final String SESSION_ID_KEY = "nanqi_gps:sessionId:"; + + /** + * sessionId 缓存时长(秒),默认 12 小时 + */ + public static final long SESSION_ID_EXPIRE_SECONDS = 12 * 60 * 60L; +} diff --git a/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/dto/gps/NanqiGpsLoginDTO.java b/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/dto/gps/NanqiGpsLoginDTO.java new file mode 100644 index 000000000..86bc94718 --- /dev/null +++ b/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/dto/gps/NanqiGpsLoginDTO.java @@ -0,0 +1,28 @@ +package com.linke.thirdParty.interfaces.dto.gps; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 南岐GPS 登录入参 + * + * @author mhd + */ +@Data +@ApiModel("南岐GPS 登录入参") +public class NanqiGpsLoginDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "登录用户/车牌号(loginType=user填用户ID,loginType=car填车牌号),为空时使用平台配置的账号", required = false) + private String userId; + + @ApiModelProperty(value = "登录密码(明文,内部会MD5小写加密),为空时使用平台配置的密码", required = false) + private String password; + + @ApiModelProperty(value = "登录类型:user-用户 car-车辆/人员,默认car", required = false) + private String loginType; +} diff --git a/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/facade/NanqiGpsApi.java b/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/facade/NanqiGpsApi.java new file mode 100644 index 000000000..68a05de21 --- /dev/null +++ b/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/facade/NanqiGpsApi.java @@ -0,0 +1,80 @@ +package com.linke.thirdParty.interfaces.facade; + +import com.linke.thirdParty.application.server.NanqiGpsApplicationService; +import com.linke.thirdParty.interfaces.vo.gps.NanqiGpsLocationVO; +import com.mhd.common.core.exception.ServiceException; +import com.mhd.common.core.web.domain.AjaxResult; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 南岐车联网GPS平台 接口 + * + * @author mhd + */ +@RestController +@RequestMapping("/nanqiGps") +@Api(tags = "南岐车联网GPS") +@Slf4j +public class NanqiGpsApi { + + @Autowired + private NanqiGpsApplicationService nanqiGpsApplicationService; + + /** + * 登录获取 sessionId + */ + @ApiOperation(value = "登录获取sessionId", notes = "登录获取南岐GPS平台sessionId(Redis缓存复用)") + @GetMapping(value = "/login") + public AjaxResult login() { + try { + String sessionId = nanqiGpsApplicationService.login(); + return AjaxResult.success("操作成功", sessionId); + } catch (ServiceException e) { + return AjaxResult.error(500, e.getMessage()); + } catch (Exception e) { + log.error("南岐GPS登录失败", e); + return AjaxResult.error(500, "操作失败" + e.getMessage()); + } + } + + /** + * 手动刷新 sessionId + */ + @ApiOperation(value = "手动刷新sessionId", notes = "强制重新登录南岐GPS平台,忽略缓存") + @GetMapping(value = "/refreshSessionId") + public AjaxResult refreshSessionId() { + try { + String sessionId = nanqiGpsApplicationService.refreshSessionId(); + return AjaxResult.success("操作成功", sessionId); + } catch (ServiceException e) { + return AjaxResult.error(500, e.getMessage()); + } catch (Exception e) { + log.error("南岐GPS刷新sessionId失败", e); + return AjaxResult.error(500, "操作失败" + e.getMessage()); + } + } + + /** + * 根据车牌号查询车辆实时定位 + */ + @ApiOperation(value = "根据车牌号查询车辆实时定位", notes = "通过车牌号查询南岐GPS车辆实时定位") + @GetMapping(value = "/queryByPlate") + public AjaxResult queryByPlate(@RequestParam(value = "carPlate") String carPlate) { + try { + NanqiGpsLocationVO vo = nanqiGpsApplicationService.queryByPlate(carPlate); + return AjaxResult.success("操作成功", vo); + } catch (ServiceException e) { + return AjaxResult.error(500, e.getMessage()); + } catch (Exception e) { + log.error("南岐GPS查询车辆[{}]定位失败", carPlate, e); + return AjaxResult.error(500, "操作失败" + e.getMessage()); + } + } +} diff --git a/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/vo/gps/NanqiGpsLocationVO.java b/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/vo/gps/NanqiGpsLocationVO.java new file mode 100644 index 000000000..2dec12d35 --- /dev/null +++ b/mhd_thrid_party/src/main/java/com/linke/thirdParty/interfaces/vo/gps/NanqiGpsLocationVO.java @@ -0,0 +1,92 @@ +package com.linke.thirdParty.interfaces.vo.gps; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 南岐GPS 车辆定位信息 VO(对应文档 表1 常用字段) + * + * @author mhd + */ +@Data +@ApiModel("南岐GPS 车辆定位信息") +public class NanqiGpsLocationVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("应答结果:1成功 0失败") + private Integer rspCode; + + @ApiModelProperty("应答描述") + private String rspDesc; + + @ApiModelProperty("车辆id") + private String carId; + + @ApiModelProperty("车牌号") + private String carPlate; + + @ApiModelProperty("车辆名") + private String carName; + + @ApiModelProperty("车队id") + private String teamId; + + @ApiModelProperty("车队名称") + private String teamName; + + @ApiModelProperty("定位时间") + private String time; + + @ApiModelProperty("经度(WGS84)") + private BigDecimal lng; + + @ApiModelProperty("纬度(WGS84)") + private BigDecimal lat; + + @ApiModelProperty("速度(千米/小时)") + private BigDecimal speed; + + @ApiModelProperty("方向 0~360") + private String drct; + + @ApiModelProperty("平台总里程(千米)") + private String mile; + + @ApiModelProperty("今日里程(千米)") + private String preMile; + + @ApiModelProperty("定位信号:0无 弱<=3 中<=6 强>6") + private String satl; + + @ApiModelProperty("通讯信号:0无 弱<=10 中<=20 强>20") + private String sgn; + + @ApiModelProperty("地理描述") + private String addr; + + @ApiModelProperty("车辆定位状态") + private String state; + + @ApiModelProperty("车辆状态文字") + private String stateCn; + + @ApiModelProperty("驾驶员") + private String drvName; + + @ApiModelProperty("驾驶员电话") + private String drvPhone; + + @ApiModelProperty("车辆类型") + private String carType; + + @ApiModelProperty("服务到期状态:1已到期 0未到期") + private String expState; + + @ApiModelProperty("服务期限") + private String expTime; +}