AppletHomeServiceImpl.java 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. package com.zsElectric.boot.business.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.core.metadata.IPage;
  4. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  5. import com.zsElectric.boot.business.mapper.BannerInfoMapper;
  6. import com.zsElectric.boot.business.mapper.ChargeOrderInfoMapper;
  7. import com.zsElectric.boot.business.mapper.DiscountsActivityMapper;
  8. import com.zsElectric.boot.business.mapper.PolicyFeeMapper;
  9. import com.zsElectric.boot.business.mapper.ThirdPartyStationInfoMapper;
  10. import com.zsElectric.boot.business.mapper.UserAccountMapper;
  11. import com.zsElectric.boot.business.mapper.UserFirmMapper;
  12. import com.zsElectric.boot.system.mapper.DictItemMapper;
  13. import com.zsElectric.boot.charging.entity.ThirdPartyChargeStatus;
  14. import com.zsElectric.boot.charging.entity.ThirdPartyConnectorInfo;
  15. import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentInfo;
  16. import com.zsElectric.boot.charging.mapper.ThirdPartyChargeStatusMapper;
  17. import com.zsElectric.boot.charging.mapper.ThirdPartyConnectorInfoMapper;
  18. import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentPricePolicy;
  19. import com.zsElectric.boot.charging.entity.ThirdPartyPolicyInfo;
  20. import com.zsElectric.boot.charging.entity.ThirdPartyStationInfo;
  21. import com.zsElectric.boot.business.mapper.ThirdPartyEquipmentInfoMapper;
  22. import com.zsElectric.boot.charging.mapper.ThirdPartyEquipmentPricePolicyMapper;
  23. import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper;
  24. import com.zsElectric.boot.business.model.entity.BannerInfo;
  25. import com.zsElectric.boot.business.model.entity.ChargeOrderInfo;
  26. import com.zsElectric.boot.business.model.entity.DiscountsActivity;
  27. import com.zsElectric.boot.business.model.entity.PolicyFee;
  28. import com.zsElectric.boot.business.model.entity.UserAccount;
  29. import com.zsElectric.boot.business.model.entity.UserFirm;
  30. import com.zsElectric.boot.system.model.entity.DictItem;
  31. import com.zsElectric.boot.business.model.query.StationInfoQuery;
  32. import com.zsElectric.boot.business.model.vo.AppletConnectorListVO;
  33. import com.zsElectric.boot.business.model.vo.AppletStationDetailVO;
  34. import com.zsElectric.boot.business.model.vo.AppletStationPriceListVO;
  35. import com.zsElectric.boot.business.model.vo.BannerInfoVO;
  36. import com.zsElectric.boot.business.model.vo.StationInfoMapVO;
  37. import com.zsElectric.boot.business.model.vo.StationInfoVO;
  38. import com.zsElectric.boot.business.model.vo.applet.AppletChargingCostVO;
  39. import com.zsElectric.boot.business.model.vo.applet.AppletConnectorDetailVO;
  40. import com.zsElectric.boot.business.model.vo.applet.AppletStationSearchVO;
  41. import com.zsElectric.boot.business.service.AppletHomeService;
  42. import com.zsElectric.boot.business.converter.BannerInfoConverter;
  43. import com.zsElectric.boot.security.util.SecurityUtils;
  44. import lombok.RequiredArgsConstructor;
  45. import lombok.extern.slf4j.Slf4j;
  46. import org.springframework.stereotype.Service;
  47. import java.math.BigDecimal;
  48. import java.math.RoundingMode;
  49. import java.time.LocalTime;
  50. import java.time.format.DateTimeFormatter;
  51. import java.util.ArrayList;
  52. import java.util.HashMap;
  53. import java.util.List;
  54. import java.util.Map;
  55. import java.util.stream.Collectors;
  56. @Slf4j
  57. @Service
  58. @RequiredArgsConstructor
  59. public class AppletHomeServiceImpl implements AppletHomeService {
  60. private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper;
  61. private final UserFirmMapper userFirmMapper;
  62. private final UserAccountMapper userAccountMapper;
  63. private final BannerInfoMapper bannerInfoMapper;
  64. private final BannerInfoConverter bannerInfoConverter;
  65. private final ThirdPartyConnectorInfoMapper thirdPartyConnectorInfoMapper;
  66. private final ThirdPartyEquipmentInfoMapper thirdPartyEquipmentInfoMapper;
  67. private final ThirdPartyEquipmentPricePolicyMapper thirdPartyEquipmentPricePolicyMapper;
  68. private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper;
  69. private final PolicyFeeMapper policyFeeMapper;
  70. private final ThirdPartyChargeStatusMapper thirdPartyChargeStatusMapper;
  71. private final ChargeOrderInfoMapper chargeOrderInfoMapper;
  72. private final DiscountsActivityMapper discountsActivityMapper;
  73. private final DictItemMapper dictItemMapper;
  74. /**
  75. * 时间格式化器 HHmmss
  76. */
  77. private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HHmmss");
  78. @Override
  79. public IPage<StationInfoVO> getStationInfoPage(StationInfoQuery queryParams) {
  80. // 查询用户是否为企业用户,获取企业ID
  81. Long firmId = null;
  82. boolean isFirmUser = false;
  83. if (queryParams.getUserId() != null) {
  84. UserFirm userFirm = userFirmMapper.selectOne(
  85. new LambdaQueryWrapper<UserFirm>()
  86. .eq(UserFirm::getUserId, queryParams.getUserId())
  87. .last("LIMIT 1")
  88. );
  89. if (userFirm != null) {
  90. firmId = userFirm.getFirmId();
  91. isFirmUser = true;
  92. }
  93. }
  94. // 获取当前时间(HHmmss格式)
  95. String currentTime = LocalTime.now().format(TIME_FORMATTER);
  96. // 构建分页对象
  97. Page<StationInfoVO> page = new Page<>(queryParams.getPageNum(), queryParams.getPageSize());
  98. // 执行查询
  99. IPage<StationInfoVO> resultPage = thirdPartyStationInfoMapper.selectAppletStationInfoPage(
  100. page, queryParams, currentTime, firmId
  101. );
  102. // 设置是否企业用户标识
  103. // 只有当用户属于企业且企业有设置价格时,才标记为企业用户
  104. final boolean finalIsFirmUser = isFirmUser;
  105. resultPage.getRecords().forEach(vo -> {
  106. // 如果是企业用户但企业价为null,说明该企业未设置价格,firmUser应为false
  107. boolean hasFirmPrice = vo.getEnterprisePrice() != null;
  108. vo.setFirmUser(finalIsFirmUser && hasFirmPrice);
  109. });
  110. return resultPage;
  111. }
  112. @Override
  113. public List<StationInfoMapVO> getStationInfoMapList(BigDecimal longitude, BigDecimal latitude) {
  114. // 获取当前时间(HHmmss格式)
  115. String currentTime = LocalTime.now().format(TIME_FORMATTER);
  116. // 查询站点列表(按距离排序)
  117. return thirdPartyStationInfoMapper.selectStationMapList(
  118. longitude, latitude, currentTime
  119. );
  120. }
  121. @Override
  122. public List<BannerInfoVO> getBannerList(Integer location) {
  123. // 查询启用状态的Banner,按排序字段降序
  124. List<BannerInfo> bannerList = bannerInfoMapper.selectList(
  125. new LambdaQueryWrapper<BannerInfo>()
  126. .eq(BannerInfo::getStatus, 1)
  127. .eq(BannerInfo::getLocation, location)
  128. .orderByDesc(BannerInfo::getSort)
  129. );
  130. return bannerInfoConverter.toVO(bannerList);
  131. }
  132. @Override
  133. public AppletStationDetailVO getStationDetail(Long stationId, BigDecimal longitude, BigDecimal latitude) {
  134. // 获取当前登录用户ID
  135. Long userId = SecurityUtils.getUserId();
  136. // 查询用户是否为企业用户,获取企业ID
  137. Long firmId = null;
  138. boolean isFirmUser = false;
  139. if (userId != null) {
  140. UserFirm userFirm = userFirmMapper.selectOne(
  141. new LambdaQueryWrapper<UserFirm>()
  142. .eq(UserFirm::getUserId, userId)
  143. .last("LIMIT 1")
  144. );
  145. if (userFirm != null) {
  146. firmId = userFirm.getFirmId();
  147. isFirmUser = true;
  148. }
  149. }
  150. // 获取当前时间(HHmmss格式)
  151. String currentTime = LocalTime.now().format(TIME_FORMATTER);
  152. // 查询站点详情基本信息
  153. AppletStationDetailVO result = thirdPartyStationInfoMapper.selectStationDetail(
  154. stationId, longitude, latitude, currentTime, firmId
  155. );
  156. if (result == null) {
  157. return null;
  158. }
  159. // 设置是否企业用户
  160. result.setFirmUser(isFirmUser);
  161. // 计算预计省多少(当前价 - 原价,如果为负则取绝对值)
  162. if (result.getCurrentPrice() != null && result.getOriginalPrice() != null) {
  163. BigDecimal saving = result.getOriginalPrice().subtract(result.getCurrentPrice());
  164. result.setEstimatedSaving(saving.compareTo(BigDecimal.ZERO) > 0 ? saving : BigDecimal.ZERO);
  165. result.setShowSpecialPriceTag(saving.compareTo(BigDecimal.ZERO) > 0);
  166. }
  167. // 查询站点对应的stationId
  168. ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectById(stationId);
  169. if (stationInfo == null) {
  170. return result;
  171. }
  172. // 根据站点stationId查询设备列表(third_party_equipment_info)
  173. List<ThirdPartyEquipmentInfo> equipmentList = thirdPartyEquipmentInfoMapper.selectList(
  174. new LambdaQueryWrapper<ThirdPartyEquipmentInfo>()
  175. .eq(ThirdPartyEquipmentInfo::getStationId, stationInfo.getStationId())
  176. );
  177. // 构建设备ID到设备类型的映射
  178. Map<String, Integer> equipmentTypeMap = equipmentList.stream()
  179. .collect(Collectors.toMap(
  180. ThirdPartyEquipmentInfo::getEquipmentId,
  181. e -> e.getEquipmentType() != null ? e.getEquipmentType() : 0,
  182. (v1, v2) -> v1
  183. ));
  184. // 根据设备列表的equipmentId查询充电接口信息(third_party_connector_info)
  185. List<ThirdPartyConnectorInfo> connectorList = new ArrayList<>();
  186. if (!equipmentList.isEmpty()) {
  187. List<String> equipmentIds = equipmentList.stream()
  188. .map(ThirdPartyEquipmentInfo::getEquipmentId)
  189. .collect(Collectors.toList());
  190. connectorList = thirdPartyConnectorInfoMapper.selectList(
  191. new LambdaQueryWrapper<ThirdPartyConnectorInfo>()
  192. .in(ThirdPartyConnectorInfo::getEquipmentId, equipmentIds)
  193. );
  194. }
  195. // 统计终端状态
  196. int idleCount = 0;
  197. int occupiedCount = 0;
  198. int offlineCount = 0;
  199. List<AppletStationDetailVO.ConnectorInfoVO> connectorVOList = new ArrayList<>();
  200. for (ThirdPartyConnectorInfo connector : connectorList) {
  201. AppletStationDetailVO.ConnectorInfoVO vo = new AppletStationDetailVO.ConnectorInfoVO();
  202. vo.setConnectorId(connector.getId());
  203. vo.setConnectorName(connector.getConnectorName());
  204. vo.setConnectorCode(connector.getConnectorId());
  205. // 设备分类:从设备表获取设备类型(1-直流设备,2-交流设备,3-交直流一体设备,4-无线设备,5-其他)
  206. Integer equipmentType = equipmentTypeMap.get(connector.getEquipmentId());
  207. if (equipmentType != null) {
  208. switch (equipmentType) {
  209. case 1:
  210. vo.setEquipmentType("直流设备");
  211. break;
  212. case 2:
  213. vo.setEquipmentType("交流设备");
  214. break;
  215. case 3:
  216. vo.setEquipmentType("交直流一体设备");
  217. break;
  218. case 4:
  219. vo.setEquipmentType("无线设备");
  220. break;
  221. case 5:
  222. vo.setEquipmentType("其他");
  223. break;
  224. default:
  225. vo.setEquipmentType("未知");
  226. }
  227. }
  228. // 从充电接口表获取实际状态
  229. // 0-离网,1-空闲,2-占用(未充电),3-占用(充电中),4-占用(预约锁定),255-故障
  230. Integer connectorStatus = connector.getStatus();
  231. if (connectorStatus == null) {
  232. // 默认为离网
  233. connectorStatus = 0;
  234. }
  235. vo.setStatus(connectorStatus);
  236. // 设置状态名称并统计
  237. switch (connectorStatus) {
  238. case 0:
  239. vo.setStatusName("离网");
  240. offlineCount++;
  241. break;
  242. case 1:
  243. vo.setStatusName("空闲");
  244. idleCount++;
  245. break;
  246. case 2:
  247. vo.setStatusName("占用(未充电)");
  248. occupiedCount++;
  249. break;
  250. case 3:
  251. vo.setStatusName("占用(充电中)");
  252. occupiedCount++;
  253. break;
  254. case 4:
  255. vo.setStatusName("占用(预约锁定)");
  256. occupiedCount++;
  257. break;
  258. case 255:
  259. vo.setStatusName("故障");
  260. offlineCount++;
  261. break;
  262. default:
  263. vo.setStatusName("未知");
  264. offlineCount++;
  265. }
  266. connectorVOList.add(vo);
  267. }
  268. result.setConnectorList(connectorVOList);
  269. result.setIdleCount(idleCount);
  270. result.setOccupiedCount(occupiedCount);
  271. result.setOfflineCount(offlineCount);
  272. return result;
  273. }
  274. @Override
  275. public AppletConnectorListVO getConnectorList(Long stationId) {
  276. // 查询站点信息
  277. ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectById(stationId);
  278. if (stationInfo == null) {
  279. return null;
  280. }
  281. AppletConnectorListVO result = new AppletConnectorListVO();
  282. result.setStationId(stationId);
  283. result.setStationName(stationInfo.getStationName());
  284. result.setTips(stationInfo.getStationTips());
  285. // 根据站点stationId查询设备列表
  286. List<ThirdPartyEquipmentInfo> equipmentList = thirdPartyEquipmentInfoMapper.selectList(
  287. new LambdaQueryWrapper<ThirdPartyEquipmentInfo>()
  288. .eq(ThirdPartyEquipmentInfo::getStationId, stationInfo.getStationId())
  289. );
  290. // 构建设备ID到设备类型的映射
  291. Map<String, Integer> equipmentTypeMap = equipmentList.stream()
  292. .collect(Collectors.toMap(
  293. ThirdPartyEquipmentInfo::getEquipmentId,
  294. e -> e.getEquipmentType() != null ? e.getEquipmentType() : 0,
  295. (v1, v2) -> v1
  296. ));
  297. // 查询充电接口信息
  298. List<ThirdPartyConnectorInfo> connectorList = new ArrayList<>();
  299. if (!equipmentList.isEmpty()) {
  300. List<String> equipmentIds = equipmentList.stream()
  301. .map(ThirdPartyEquipmentInfo::getEquipmentId)
  302. .collect(Collectors.toList());
  303. connectorList = thirdPartyConnectorInfoMapper.selectList(
  304. new LambdaQueryWrapper<ThirdPartyConnectorInfo>()
  305. .in(ThirdPartyConnectorInfo::getEquipmentId, equipmentIds)
  306. );
  307. }
  308. // 统计终端状态
  309. int idleCount = 0;
  310. int occupiedCount = 0;
  311. int offlineCount = 0;
  312. List<AppletConnectorListVO.ConnectorItemVO> connectorVOList = new ArrayList<>();
  313. for (ThirdPartyConnectorInfo connector : connectorList) {
  314. AppletConnectorListVO.ConnectorItemVO vo = new AppletConnectorListVO.ConnectorItemVO();
  315. vo.setConnectorId(connector.getId());
  316. vo.setConnectorName(connector.getConnectorName());
  317. vo.setConnectorCode(connector.getConnectorId());
  318. // 设备分类
  319. Integer equipmentType = equipmentTypeMap.get(connector.getEquipmentId());
  320. vo.setEquipmentType(getEquipmentTypeName(equipmentType));
  321. // 状态处理
  322. Integer connectorStatus = connector.getStatus();
  323. if (connectorStatus == null) {
  324. connectorStatus = 0;
  325. }
  326. vo.setStatus(connectorStatus);
  327. vo.setStatusName(getStatusName(connectorStatus));
  328. // 统计
  329. switch (connectorStatus) {
  330. case 1:
  331. idleCount++;
  332. break;
  333. case 2:
  334. case 3:
  335. case 4:
  336. occupiedCount++;
  337. break;
  338. default:
  339. offlineCount++;
  340. }
  341. connectorVOList.add(vo);
  342. }
  343. result.setConnectorList(connectorVOList);
  344. result.setIdleCount(idleCount);
  345. result.setOccupiedCount(occupiedCount);
  346. result.setOfflineCount(offlineCount);
  347. return result;
  348. }
  349. @Override
  350. public AppletStationPriceListVO getStationPriceList(Long stationId) {
  351. // 查询站点信息
  352. ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectById(stationId);
  353. if (stationInfo == null) {
  354. log.warn("站点信息不存在,stationId: {}", stationId);
  355. return null;
  356. }
  357. log.info("查询站点价格列表,stationId: {}, stationName: {}, station_id字段: {}",
  358. stationId, stationInfo.getStationName(), stationInfo.getStationId());
  359. AppletStationPriceListVO result = new AppletStationPriceListVO();
  360. result.setStationId(stationId);
  361. result.setStationName(stationInfo.getStationName());
  362. result.setTips(stationInfo.getStationTips());
  363. // 获取当前登录用户ID
  364. Long userId = SecurityUtils.getUserId();
  365. log.info("当前用户ID: {}", userId);
  366. // 查询用户是否为企业用户,获取企业ID
  367. Long firmId = null;
  368. if (userId != null) {
  369. UserFirm userFirm = userFirmMapper.selectOne(
  370. new LambdaQueryWrapper<UserFirm>()
  371. .eq(UserFirm::getUserId, userId)
  372. .last("LIMIT 1")
  373. );
  374. if (userFirm != null) {
  375. firmId = userFirm.getFirmId();
  376. log.info("用户所属企业ID: {}", firmId);
  377. }
  378. }
  379. // 获取当前时间(HHmmss格式)
  380. String currentTime = LocalTime.now().format(TIME_FORMATTER);
  381. log.info("当前时间: {}", currentTime);
  382. // 查询站点对应的价格策略
  383. // 1. 先查询充电接口
  384. ThirdPartyConnectorInfo connector = thirdPartyConnectorInfoMapper.selectOne(
  385. new LambdaQueryWrapper<ThirdPartyConnectorInfo>()
  386. .eq(ThirdPartyConnectorInfo::getStationId, stationInfo.getStationId())
  387. .last("LIMIT 1")
  388. );
  389. if (connector == null) {
  390. log.warn("未找到充电接口信息,station_id: {}", stationInfo.getStationId());
  391. result.setPriceList(new ArrayList<>());
  392. return result;
  393. }
  394. log.info("找到充电接口,connectorId: {}", connector.getConnectorId());
  395. // 2. 查询价格策略
  396. ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(
  397. new LambdaQueryWrapper<ThirdPartyEquipmentPricePolicy>()
  398. .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, connector.getConnectorId())
  399. .eq(ThirdPartyEquipmentPricePolicy::getIsDeleted, 0)
  400. .last("LIMIT 1")
  401. );
  402. if (pricePolicy == null) {
  403. log.warn("未找到价格策略,connectorId: {}", connector.getConnectorId());
  404. result.setPriceList(new ArrayList<>());
  405. return result;
  406. }
  407. log.info("找到价格策略,pricePolicyId: {}", pricePolicy.getId());
  408. // 3. 查询价格明细列表
  409. List<ThirdPartyPolicyInfo> policyInfoList = thirdPartyPolicyInfoMapper.selectList(
  410. new LambdaQueryWrapper<ThirdPartyPolicyInfo>()
  411. .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId())
  412. .eq(ThirdPartyPolicyInfo::getIsDeleted, 0)
  413. .orderByAsc(ThirdPartyPolicyInfo::getStartTime)
  414. );
  415. log.info("查询到价格明细记录数: {}", policyInfoList.size());
  416. // 如果没有查询到价格策略明细,说明该站点未配置价格策略,直接返回空列表
  417. if (policyInfoList.isEmpty()) {
  418. log.warn("third_party_policy_info表中没有价格策略数据,pricePolicyId: {}", pricePolicy.getId());
  419. result.setPriceList(new ArrayList<>());
  420. result.setHasEnterprisePrice(false);
  421. return result;
  422. }
  423. // 4. 如果是企业用户,查询企业价格
  424. Map<String, BigDecimal> enterprisePriceMap = new HashMap<>();
  425. boolean hasEnterprisePrice = false;
  426. if (firmId != null) {
  427. List<PolicyFee> policyFeeList = policyFeeMapper.selectList(
  428. new LambdaQueryWrapper<PolicyFee>()
  429. .eq(PolicyFee::getStationInfoId, stationId)
  430. .eq(PolicyFee::getSalesType, 1) // 1-企业
  431. .eq(PolicyFee::getFirmId, firmId)
  432. .eq(PolicyFee::getIsDeleted, 0)
  433. );
  434. log.info("查询企业价格记录数: {}, stationInfoId: {}, firmId: {}",
  435. policyFeeList.size(), stationId, firmId);
  436. // 如果查询到企业价格记录,设置标志为true
  437. if (!policyFeeList.isEmpty()) {
  438. hasEnterprisePrice = true;
  439. }
  440. // 构建企业价格映射 (startTime -> enterprisePrice)
  441. for (PolicyFee policyFee : policyFeeList) {
  442. // 查找对应的时段价格信息
  443. ThirdPartyPolicyInfo matchedPolicyInfo = policyInfoList.stream()
  444. .filter(info -> info.getStartTime().equals(policyFee.getStartTime()))
  445. .findFirst()
  446. .orElse(null);
  447. if (matchedPolicyInfo != null) {
  448. // 企业价格 = 电费 + 服务费 + 运营费 + 综合销售费
  449. BigDecimal elecPrice = matchedPolicyInfo.getElecPrice() != null ? matchedPolicyInfo.getElecPrice() : BigDecimal.ZERO;
  450. BigDecimal servicePrice = matchedPolicyInfo.getServicePrice() != null ? matchedPolicyInfo.getServicePrice() : BigDecimal.ZERO;
  451. BigDecimal opFee = policyFee.getOpFee() != null ? policyFee.getOpFee() : BigDecimal.ZERO;
  452. BigDecimal compSalesFee = policyFee.getCompSalesFee() != null ? policyFee.getCompSalesFee() : BigDecimal.ZERO;
  453. BigDecimal enterprisePrice = elecPrice.add(servicePrice).add(opFee).add(compSalesFee);
  454. enterprisePriceMap.put(policyFee.getStartTime(), enterprisePrice);
  455. log.debug("企业价格计算 - 时段: {}, 电费: {}, 服务费: {}, 运营费: {}, 综合销售费: {}, 企业价: {}",
  456. policyFee.getStartTime(), elecPrice, servicePrice, opFee, compSalesFee, enterprisePrice);
  457. } else {
  458. log.warn("未找到匹配的价格策略明细,startTime: {}", policyFee.getStartTime());
  459. }
  460. }
  461. }
  462. // 设置是否有企业价格
  463. result.setHasEnterprisePrice(hasEnterprisePrice);
  464. log.info("是否有企业价格: {}", hasEnterprisePrice);
  465. // 5. 构建价格列表
  466. List<AppletStationPriceListVO.PriceItemVO> priceList = new ArrayList<>();
  467. for (int i = 0; i < policyInfoList.size(); i++) {
  468. ThirdPartyPolicyInfo policyInfo = policyInfoList.get(i);
  469. AppletStationPriceListVO.PriceItemVO priceItem = new AppletStationPriceListVO.PriceItemVO();
  470. // 时段格式化:HHmmss -> HH:mm
  471. String startTime = formatTime(policyInfo.getStartTime());
  472. // 下一个时段的开始时间作为当前时段的结束时间
  473. String endTime = (i + 1 < policyInfoList.size())
  474. ? formatTime(policyInfoList.get(i + 1).getStartTime())
  475. : "24:00";
  476. priceItem.setTimePeriod(startTime + "-" + endTime);
  477. priceItem.setPeriodFlag(policyInfo.getPeriodFlag());
  478. priceItem.setPeriodFlagName(getPeriodFlagName(policyInfo.getPeriodFlag()));
  479. priceItem.setElecPrice(policyInfo.getElecPrice());
  480. priceItem.setServicePrice(policyInfo.getServicePrice());
  481. // 合计充电价 = 电费 + 服务费
  482. BigDecimal elecPrice = policyInfo.getElecPrice() != null ? policyInfo.getElecPrice() : BigDecimal.ZERO;
  483. BigDecimal servicePrice = policyInfo.getServicePrice() != null ? policyInfo.getServicePrice() : BigDecimal.ZERO;
  484. priceItem.setTotalPrice(elecPrice.add(servicePrice));
  485. // 设置企业价格(如果有)
  486. BigDecimal enterprisePrice = enterprisePriceMap.get(policyInfo.getStartTime());
  487. priceItem.setEnterprisePrice(enterprisePrice);
  488. // 判断是否为当前时段
  489. priceItem.setCurrentPeriod(isCurrentPeriod(policyInfo.getStartTime(),
  490. (i + 1 < policyInfoList.size()) ? policyInfoList.get(i + 1).getStartTime() : null,
  491. currentTime));
  492. priceList.add(priceItem);
  493. }
  494. result.setPriceList(priceList);
  495. return result;
  496. }
  497. /**
  498. * 获取设备类型名称
  499. */
  500. private String getEquipmentTypeName(Integer equipmentType) {
  501. if (equipmentType == null) {
  502. return "未知";
  503. }
  504. switch (equipmentType) {
  505. case 1:
  506. return "直流设备";
  507. case 2:
  508. return "交流设备";
  509. case 3:
  510. return "交直流一体设备";
  511. case 4:
  512. return "无线设备";
  513. case 5:
  514. return "其他";
  515. default:
  516. return "未知";
  517. }
  518. }
  519. /**
  520. * 获取状态名称
  521. */
  522. private String getStatusName(Integer status) {
  523. if (status == null) {
  524. return "未知";
  525. }
  526. switch (status) {
  527. case 0:
  528. return "离网";
  529. case 1:
  530. return "空闲";
  531. case 2:
  532. return "占用(未充电)";
  533. case 3:
  534. return "占用(充电中)";
  535. case 4:
  536. return "占用(预约锁定)";
  537. case 255:
  538. return "故障";
  539. default:
  540. return "未知";
  541. }
  542. }
  543. /**
  544. * 获取时段标志名称
  545. */
  546. private String getPeriodFlagName(Integer periodFlag) {
  547. if (periodFlag == null) {
  548. return "";
  549. }
  550. switch (periodFlag) {
  551. case 1:
  552. return "尖";
  553. case 2:
  554. return "峰";
  555. case 3:
  556. return "平";
  557. case 4:
  558. return "谷";
  559. default:
  560. return "";
  561. }
  562. }
  563. /**
  564. * 格式化时间 HHmmss -> HH:mm
  565. */
  566. private String formatTime(String time) {
  567. if (time == null || time.length() < 4) {
  568. return "00:00";
  569. }
  570. return time.substring(0, 2) + ":" + time.substring(2, 4);
  571. }
  572. /**
  573. * 判断是否为当前时段
  574. */
  575. private boolean isCurrentPeriod(String startTime, String nextStartTime, String currentTime) {
  576. if (startTime == null || currentTime == null) {
  577. return false;
  578. }
  579. // 当前时间 >= 开始时间 且 当前时间 < 下一个时段开始时间
  580. if (currentTime.compareTo(startTime) >= 0) {
  581. if (nextStartTime == null || currentTime.compareTo(nextStartTime) < 0) {
  582. return true;
  583. }
  584. }
  585. return false;
  586. }
  587. @Override
  588. public AppletChargingCostVO getCurrentChargingCost() {
  589. // 获取当前登录用户ID
  590. Long userId = SecurityUtils.getUserId();
  591. if (userId == null) {
  592. return null;
  593. }
  594. return thirdPartyStationInfoMapper.selectCurrentChargingCost(userId);
  595. }
  596. @Override
  597. public List<AppletStationSearchVO> searchStations(String keyword, BigDecimal longitude, BigDecimal latitude) {
  598. // 关键词为空或空字符串,返回空列表
  599. if (keyword == null || keyword.trim().isEmpty()) {
  600. return new ArrayList<>();
  601. }
  602. // 调用Mapper查询
  603. return thirdPartyStationInfoMapper.searchStations(keyword.trim(), longitude, latitude);
  604. }
  605. @Override
  606. public AppletConnectorDetailVO getConnectorDetail(Long connectorId) {
  607. // 获取当前登录用户ID
  608. Long userId = SecurityUtils.getUserId();
  609. // 获取当前时间(HHmmss格式)
  610. String currentTime = LocalTime.now().format(TIME_FORMATTER);
  611. // 查询用户余额
  612. BigDecimal userBalance = null;
  613. if (userId != null) {
  614. UserAccount userAccount = userAccountMapper.selectOne(
  615. new LambdaQueryWrapper<UserAccount>()
  616. .eq(UserAccount::getUserId, userId)
  617. .eq(UserAccount::getIsDeleted, 0)
  618. );
  619. if (userAccount != null) {
  620. userBalance = userAccount.getBalance();
  621. }
  622. }
  623. // 查询新用户优惠金额(仅当用户为新用户时)
  624. BigDecimal newUserDiscount = null;
  625. if (userId != null) {
  626. // 检查用户是否有充电订单
  627. Long orderCount = chargeOrderInfoMapper.selectCount(
  628. new LambdaQueryWrapper<ChargeOrderInfo>()
  629. .eq(ChargeOrderInfo::getUserId, userId)
  630. .eq(ChargeOrderInfo::getIsDeleted, 0)
  631. );
  632. // 如果是新用户(没有订单),查询新用户首单优惠金额
  633. if (orderCount == 0) {
  634. // 查询c_discounts_activity表,type=1(新用户首单立减)且status=1(启用)的数据
  635. List<DiscountsActivity> discountActivities = discountsActivityMapper.selectList(
  636. new LambdaQueryWrapper<DiscountsActivity>()
  637. .eq(DiscountsActivity::getType, 1) // 1-新用户首单立减
  638. .eq(DiscountsActivity::getStatus, 1) // 1-启用
  639. .eq(DiscountsActivity::getIsDeleted, 0)
  640. );
  641. // 计算所有启用的新用户优惠金额总和
  642. if (!discountActivities.isEmpty()) {
  643. newUserDiscount = discountActivities.stream()
  644. .map(DiscountsActivity::getDiscount)
  645. .filter(discount -> discount != null)
  646. .reduce(BigDecimal.ZERO, BigDecimal::add);
  647. log.info("新用户首单优惠金额 - userId: {}, discount: {}", userId, newUserDiscount);
  648. }
  649. }
  650. }
  651. // 调用Mapper方法,一次SQL查询获取所有信息
  652. AppletConnectorDetailVO result = thirdPartyConnectorInfoMapper.selectConnectorDetailById(
  653. connectorId, userId, currentTime, userBalance, newUserDiscount
  654. );
  655. if (result == null) {
  656. log.warn("充电接口不存在,connectorId: {}", connectorId);
  657. }
  658. return result;
  659. }
  660. @Override
  661. public BigDecimal calculateAvailableChargingAmount(Long connectorId) {
  662. // 获取当前登录用户ID
  663. Long userId = SecurityUtils.getUserId();
  664. if (userId == null) {
  665. log.warn("未登录用户,无法计算可用充电金额");
  666. return BigDecimal.ZERO;
  667. }
  668. // 1. 查询当前用户余额
  669. UserAccount userAccount = userAccountMapper.selectOne(
  670. new LambdaQueryWrapper<UserAccount>()
  671. .eq(UserAccount::getUserId, userId)
  672. .eq(UserAccount::getIsDeleted, 0)
  673. );
  674. if (userAccount == null || userAccount.getBalance() == null) {
  675. log.warn("用户账户不存在或余额为空 - userId: {}", userId);
  676. return BigDecimal.ZERO;
  677. }
  678. BigDecimal userBalance = userAccount.getBalance();
  679. // 2. 从字典表查询安全价(safety_fee)
  680. DictItem safetyFeeItem = dictItemMapper.selectOne(
  681. new LambdaQueryWrapper<DictItem>()
  682. .eq(DictItem::getDictCode, "safety_fee")
  683. .eq(DictItem::getStatus, 1)
  684. .last("LIMIT 1")
  685. );
  686. BigDecimal safetyFee = safetyFeeItem != null && safetyFeeItem.getValue() != null
  687. ? new BigDecimal(safetyFeeItem.getValue())
  688. : BigDecimal.ZERO;
  689. // 计算扣除安全价后的余额
  690. BigDecimal balanceAfterSafety = userBalance.subtract(safetyFee);
  691. if (balanceAfterSafety.compareTo(BigDecimal.ZERO) <= 0) {
  692. log.info("用户余额不足 - userId: {}, balance: {}, safetyFee: {}", userId, userBalance, safetyFee);
  693. return BigDecimal.ZERO;
  694. }
  695. // 3. 查询充电接口、设备、站点信息
  696. ThirdPartyConnectorInfo connectorInfo = thirdPartyConnectorInfoMapper.selectById(connectorId);
  697. if (connectorInfo == null) {
  698. log.warn("充电接口不存在 - connectorId: {}", connectorId);
  699. return BigDecimal.ZERO;
  700. }
  701. ThirdPartyEquipmentInfo equipmentInfo = thirdPartyEquipmentInfoMapper.selectOne(
  702. new LambdaQueryWrapper<ThirdPartyEquipmentInfo>()
  703. .eq(ThirdPartyEquipmentInfo::getEquipmentId, connectorInfo.getEquipmentId())
  704. .eq(ThirdPartyEquipmentInfo::getIsDeleted, 0)
  705. .last("LIMIT 1")
  706. );
  707. if (equipmentInfo == null) {
  708. log.warn("设备信息不存在 - equipmentId: {}", connectorInfo.getEquipmentId());
  709. return BigDecimal.ZERO;
  710. }
  711. ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
  712. new LambdaQueryWrapper<ThirdPartyStationInfo>()
  713. .eq(ThirdPartyStationInfo::getStationId, equipmentInfo.getStationId())
  714. .eq(ThirdPartyStationInfo::getIsDeleted, 0)
  715. .last("LIMIT 1")
  716. );
  717. if (stationInfo == null) {
  718. log.warn("站点信息不存在 - stationId: {}", equipmentInfo.getStationId());
  719. return BigDecimal.ZERO;
  720. }
  721. // 4. 查询价格策略
  722. ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(
  723. new LambdaQueryWrapper<ThirdPartyEquipmentPricePolicy>()
  724. .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, connectorInfo.getConnectorId())
  725. .eq(ThirdPartyEquipmentPricePolicy::getIsDeleted, 0)
  726. .last("LIMIT 1")
  727. );
  728. if (pricePolicy == null) {
  729. log.warn("价格策略不存在 - connectorId: {}", connectorInfo.getConnectorId());
  730. return BigDecimal.ZERO;
  731. }
  732. // 5. 查询所有时段的价格信息(按开始时间排序)
  733. List<ThirdPartyPolicyInfo> policyInfoList = thirdPartyPolicyInfoMapper.selectList(
  734. new LambdaQueryWrapper<ThirdPartyPolicyInfo>()
  735. .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId())
  736. .eq(ThirdPartyPolicyInfo::getIsDeleted, 0)
  737. .orderByAsc(ThirdPartyPolicyInfo::getStartTime)
  738. );
  739. if (policyInfoList.isEmpty()) {
  740. log.warn("价格策略明细不存在 - pricePolicyId: {}", pricePolicy.getId());
  741. return BigDecimal.ZERO;
  742. }
  743. // 6. 查询用户是否为企业用户
  744. UserFirm userFirm = userFirmMapper.selectOne(
  745. new LambdaQueryWrapper<UserFirm>()
  746. .eq(UserFirm::getUserId, userId)
  747. .eq(UserFirm::getIsDeleted, 0)
  748. .last("LIMIT 1")
  749. );
  750. // 7. 跨时段计算可用充电金额
  751. // 公式:可用余额 = 当前用户余额 - 安全价 - [(度数 × 运营费) + (度数 × 增值费用)]
  752. // 其中:度数 = (余额 - 安全价) / (电费 + 服务费)
  753. BigDecimal remainingBalance = balanceAfterSafety; // 剩余可用余额(用于计算度数)
  754. BigDecimal totalOpFeeCost = BigDecimal.ZERO; // 总运营费
  755. BigDecimal totalValueAddedCost = BigDecimal.ZERO; // 总增值费用
  756. // 遍历每个时段,计算在该时段能充多少度电及对应的费用
  757. for (ThirdPartyPolicyInfo policyInfo : policyInfoList) {
  758. // 获取电费、服务费
  759. BigDecimal elecPrice = policyInfo.getElecPrice() != null ? policyInfo.getElecPrice() : BigDecimal.ZERO;
  760. BigDecimal servicePrice = policyInfo.getServicePrice() != null ? policyInfo.getServicePrice() : BigDecimal.ZERO;
  761. BigDecimal basePrice = elecPrice.add(servicePrice); // 基础价格 = 电费 + 服务费
  762. if (basePrice.compareTo(BigDecimal.ZERO) == 0) {
  763. continue; // 跳过基础价格为0的时段
  764. }
  765. // 查询该时段的运营费
  766. PolicyFee policyFee;
  767. if (userFirm != null) {
  768. policyFee = policyFeeMapper.selectOne(
  769. new LambdaQueryWrapper<PolicyFee>()
  770. .eq(PolicyFee::getStationInfoId, stationInfo.getId())
  771. .eq(PolicyFee::getStartTime, policyInfo.getStartTime())
  772. .eq(PolicyFee::getSalesType, 1)
  773. .eq(PolicyFee::getFirmId, userFirm.getFirmId())
  774. .eq(PolicyFee::getIsDeleted, 0)
  775. .last("LIMIT 1")
  776. );
  777. } else {
  778. policyFee = policyFeeMapper.selectOne(
  779. new LambdaQueryWrapper<PolicyFee>()
  780. .eq(PolicyFee::getStationInfoId, stationInfo.getId())
  781. .eq(PolicyFee::getStartTime, policyInfo.getStartTime())
  782. .eq(PolicyFee::getSalesType, 0)
  783. .eq(PolicyFee::getIsDeleted, 0)
  784. .last("LIMIT 1")
  785. );
  786. }
  787. BigDecimal opFee = (policyFee != null && policyFee.getOpFee() != null)
  788. ? policyFee.getOpFee()
  789. : BigDecimal.ZERO;
  790. // 查询增值费用
  791. BigDecimal valueAddedFee = BigDecimal.ZERO;
  792. if (policyInfo.getPeriodFlag() != null) {
  793. String periodLabel = "";
  794. switch (policyInfo.getPeriodFlag()) {
  795. case 1: periodLabel = "尖"; break;
  796. case 2: periodLabel = "峰"; break;
  797. case 3: periodLabel = "平"; break;
  798. case 4: periodLabel = "谷"; break;
  799. }
  800. if (!periodLabel.isEmpty()) {
  801. DictItem valueAddedItem = dictItemMapper.selectOne(
  802. new LambdaQueryWrapper<DictItem>()
  803. .eq(DictItem::getDictCode, "time_period_flag")
  804. .eq(DictItem::getLabel, periodLabel)
  805. .eq(DictItem::getStatus, 1)
  806. .last("LIMIT 1")
  807. );
  808. if (valueAddedItem != null && valueAddedItem.getValue() != null) {
  809. valueAddedFee = new BigDecimal(valueAddedItem.getValue());
  810. }
  811. }
  812. }
  813. // 计算在当前时段能充多少度电(使用剩余余额)
  814. // 度数 = 剩余余额 / (电费 + 服务费)
  815. BigDecimal kwh = remainingBalance.divide(basePrice, 4, RoundingMode.HALF_UP);
  816. // 如果剩余余额不足以充电,结束计算
  817. if (kwh.compareTo(BigDecimal.ZERO) <= 0) {
  818. break;
  819. }
  820. // 计算该时段的运营费和增值费用
  821. BigDecimal periodOpFeeCost = kwh.multiply(opFee);
  822. BigDecimal periodValueAddedCost = kwh.multiply(valueAddedFee);
  823. totalOpFeeCost = totalOpFeeCost.add(periodOpFeeCost);
  824. totalValueAddedCost = totalValueAddedCost.add(periodValueAddedCost);
  825. // 更新剩余余额(减去当前时段的所有费用)
  826. // 当前时段总费用 = (电费 + 服务费) * 度数 + 运营费 + 增值费用
  827. BigDecimal periodBaseCost = kwh.multiply(basePrice);
  828. BigDecimal periodTotalCost = periodBaseCost.add(periodOpFeeCost).add(periodValueAddedCost);
  829. remainingBalance = remainingBalance.subtract(periodTotalCost);
  830. log.debug("时段计算 - startTime: {}, 电费: {}, 服务费: {}, 运营费: {}, 增值费: {}, 度数: {}, 该时段费用: {}, 剩余余额: {}",
  831. policyInfo.getStartTime(), elecPrice, servicePrice, opFee, valueAddedFee, kwh, periodTotalCost, remainingBalance);
  832. // 如果剩余余额小于等于0,结束计算
  833. if (remainingBalance.compareTo(BigDecimal.ZERO) <= 0) {
  834. break;
  835. }
  836. }
  837. // 8. 计算最终可用余额
  838. BigDecimal availableAmount = userBalance.subtract(safetyFee).subtract(totalOpFeeCost).subtract(totalValueAddedCost);
  839. // 确保不为负数
  840. if (availableAmount.compareTo(BigDecimal.ZERO) < 0) {
  841. availableAmount = BigDecimal.ZERO;
  842. }
  843. // 保留 2 位小数
  844. availableAmount = availableAmount.setScale(2, RoundingMode.HALF_UP);
  845. log.info("可用充电金额计算完成 - userId: {}, connectorId: {}, userBalance: {}, safetyFee: {}, " +
  846. "totalOpFeeCost: {}, totalValueAddedCost: {}, availableAmount: {}",
  847. userId, connectorId, userBalance, safetyFee, totalOpFeeCost, totalValueAddedCost, availableAmount);
  848. return availableAmount;
  849. }
  850. }