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平台 对接应用服务
+ *
+ *
提供两个接口:
+ *
+ * - login:登录获取 sessionId,并缓存到 Redis
+ * - queryByPlate:根据车牌号+sessionId 查询车辆实时定位
+ *
+ *
+ * @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;
+}