ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/TChargingGunVO.java
@@ -30,4 +30,13 @@ @ApiModelProperty(value = "结束充电权限") private boolean authEndCharge = true; @ApiModelProperty(value = "siteName") private String siteName; @ApiModelProperty(value = "partnerName") private String partnerName; @ApiModelProperty(value = "chargingPileCode") private String chargingPileCode; } ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/SiteController.java
@@ -558,8 +558,38 @@ @RequiresPermissions(value = {"/site/update"}, logical = Logical.OR) @ResponseBody @PostMapping("/getReservationSetting/{id}") @ApiOperation(value = "获取站点预约设置", tags = {"管理后台-站点管理"}) public AjaxResult<Site> getReservationSetting(@PathVariable Integer id){ Site site = siteService.lambdaQuery() .select(Site::getId, Site::getName, Site::getSupportOrder, Site::getReservationStartTime, Site::getReservationEndTime) .eq(Site::getId, id) .eq(Site::getDelFlag, 0) .one(); return AjaxResult.success(site); } @RequiresPermissions(value = {"/site/update"}, logical = Logical.OR) @ResponseBody @PostMapping("/saveReservationSetting") @ApiOperation(value = "保存站点预约设置", tags = {"管理后台-站点管理"}) public AjaxResult saveReservationSetting(@RequestBody Site site){ if (site == null || site.getId() == null) { return AjaxResult.error("站点id不能为空"); } boolean success = siteService.lambdaUpdate() .eq(Site::getId, site.getId()) .eq(Site::getDelFlag, 0) .set(Site::getSupportOrder, site.getSupportOrder()) .set(Site::getReservationStartTime, site.getReservationStartTime()) .set(Site::getReservationEndTime, site.getReservationEndTime()) .update(); return success ? AjaxResult.success() : AjaxResult.error("保存预约设置失败"); } @RequiresPermissions(value = {"/site/update"}, logical = Logical.OR) @ResponseBody @PostMapping("/editSite") @ApiOperation(value = "编辑站点", tags = {"管理后台-站点管理"}) @Log(title = "【站点管理】编辑站点", businessType = BusinessType.UPDATE) ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingGunController.java
@@ -41,6 +41,8 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * <p> @@ -226,6 +228,49 @@ e.printStackTrace(); } } @RequiresPermissions(value = {"/chargingGun/download_qr_code"}, logical = Logical.OR) @ApiOperation(tags = {"管理后台-接口信息"}, value = "批量导出接口二维码") @PutMapping(value = "/exportQRCodeZip") @Log(title = "【接口信息】批量导出接口二维码", businessType = BusinessType.EXPORT, operatorType = OperatorType.MANAGE) public void exportQRCodeZip(@RequestBody TChargingGunQuery query, HttpServletResponse response) { try { List<TChargingGunVO> list = chargingGunService.exportQRCodeList(query == null ? new TChargingGunQuery() : query); String zipName = URLEncoder.encode("接口二维码.zip", "UTF-8"); response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment;filename=" + zipName); List<String> names = new ArrayList<>(); try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { for (TChargingGunVO gun : list) { String code = StringUtils.isNotEmpty(gun.getFullNumber()) ? gun.getFullNumber() : gun.getChargingPileCode() + gun.getCode(); if (StringUtils.isEmpty(code)) { continue; } String baseName = sanitizeFileName((StringUtils.isNotEmpty(gun.getSiteName()) ? gun.getSiteName() : "电站") + (StringUtils.isNotEmpty(gun.getName()) ? gun.getName() : code)); String entryName = baseName + ".jpg"; int index = 1; while (names.contains(entryName)) { entryName = baseName + "(" + index++ + ").jpg"; } names.add(entryName); zipOut.putNextEntry(new ZipEntry(entryName)); QRCodeUtils.encode("https://mxcd.zhinenganguan.com?No=" + code, zipOut); zipOut.closeEntry(); } zipOut.finish(); } } catch (Exception e) { e.printStackTrace(); } } private String sanitizeFileName(String name) { if (StringUtils.isEmpty(name)) { return "qrcode"; } return name.replaceAll("[\\\\/:*?\\\"<>|]", "_"); } @Resource private TokenService tokenService; /** ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java
@@ -18,6 +18,7 @@ import com.ruoyi.chargingPile.dto.ChargingGunCountMonitoring; import com.ruoyi.chargingPile.dto.ChargingGunMonitoring; import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring; import com.ruoyi.chargingPile.dto.ChargingPileBatchBuildDTO; import com.ruoyi.chargingPile.dto.ChargingPileImportDTO; import com.ruoyi.chargingPile.dto.ChargingPileImportResultDTO; import com.ruoyi.chargingPile.export.ChargingPileExport; @@ -182,8 +183,8 @@ @RequiresPermissions(value = {"/chargingPile/export", "/chargingPile"}, logical = Logical.OR) @ResponseBody @PutMapping("/export") @Log(title = "??????????????", businessType = BusinessType.EXPORT,operatorType = OperatorType.MANAGE) @ApiOperation(value = "???????", tags = {"????-?????"}) @Log(title = "【充电桩信息】导出充电桩数据", businessType = BusinessType.EXPORT,operatorType = OperatorType.MANAGE) @ApiOperation(value = "导出充电桩数据", tags = {"管理后台-充电桩信息"}) public void export(@RequestBody PageChargingPileList page, HttpServletResponse response){ List<PageChargingPileListDTO> records = chargingPileService.listChargingPileForExport(page); List<ChargingPileExport> exports = records.stream().map(record -> { @@ -192,36 +193,45 @@ return export; }).collect(Collectors.toList()); ExcelUtil<ChargingPileExport> util = new ExcelUtil<>(ChargingPileExport.class); util.exportExcel(response, exports, "?????"); util.exportExcel(response, exports, "充电桩数据"); } @RequiresPermissions(value = {"/chargingPile/add"}, logical = Logical.OR) @ResponseBody @GetMapping("/importTemplate") @ApiOperation(value = "?????????", tags = {"????-?????"}) @ApiOperation(value = "下载充电桩导入模板", tags = {"管理后台-充电桩信息"}) public void importTemplate(HttpServletResponse response){ ExcelUtil<ChargingPileImportDTO> util = new ExcelUtil<>(ChargingPileImportDTO.class); util.importTemplateExcel(response, "???????"); util.importTemplateExcel(response, "充电桩导入模板"); } @RequiresPermissions(value = {"/chargingPile/add"}, logical = Logical.OR) @ResponseBody @PostMapping("/import") @Log(title = "??????????????", businessType = BusinessType.IMPORT,operatorType = OperatorType.MANAGE) @ApiOperation(value = "???????", tags = {"????-?????"}) @Log(title = "【充电桩信息】批量导入充电桩", businessType = BusinessType.IMPORT,operatorType = OperatorType.MANAGE) @ApiOperation(value = "批量导入充电桩", tags = {"管理后台-充电桩信息"}) public AjaxResult importChargingPile(@RequestParam("file") MultipartFile file, @RequestParam(value = "cover", defaultValue = "false") Boolean cover) throws Exception{ ChargingPileImportResultDTO result = chargingPileService.importChargingPile(file, cover); if(Boolean.TRUE.equals(result.getNeedConfirm())){ return AjaxResult.warn("????????????", result); return AjaxResult.warn("检测到重复设备编码,是否覆盖?", result); } if(result.getErrorCount() != null && result.getErrorCount() > 0){ return AjaxResult.error("????????????", result); return AjaxResult.error("导入失败,请检查错误信息", result); } return AjaxResult.success("????", result); return AjaxResult.success("导入成功", result); } @RequiresPermissions(value = {"/chargingPile/add"}, logical = Logical.OR) @ResponseBody @PostMapping("/batchBuild") @Log(title = "【充电桩信息】批量建桩", businessType = BusinessType.INSERT,operatorType = OperatorType.MANAGE) @ApiOperation(value = "批量建桩", tags = {"管理后台-充电桩信息"}) public AjaxResult batchBuild(@RequestBody ChargingPileBatchBuildDTO dto){ return AjaxResult.success("批量建桩成功", chargingPileService.batchBuildChargingPile(dto)); } @RequiresPermissions(value = {"/chargingPile/select", "/chargingPile/update"}, logical = Logical.OR) @ResponseBody @GetMapping("/getChargingPile/{id}") ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/ChargingPileBatchBuildDTO.java
New file @@ -0,0 +1,30 @@ package com.ruoyi.chargingPile.dto; import com.ruoyi.chargingPile.api.dto.TChargingGunDTO; import com.ruoyi.chargingPile.api.model.TChargingPile; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.List; @Data public class ChargingPileBatchBuildDTO { @ApiModelProperty("充电桩模板信息") private TChargingPile chargingPile; @ApiModelProperty("接口模板信息") private TChargingGunDTO chargingGun; @ApiModelProperty("设备编码列表") private List<String> codes; @ApiModelProperty("设备名称固定内容") private String namePrefix; @ApiModelProperty("桩号起始值") private Integer numberStart; @ApiModelProperty("单桩接口数") private Integer gunCount; } ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/ChargingPileBatchBuildResultDTO.java
New file @@ -0,0 +1,14 @@ package com.ruoyi.chargingPile.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data public class ChargingPileBatchBuildResultDTO { @ApiModelProperty("新建充电桩数量") private Integer pileCount = 0; @ApiModelProperty("新建接口数量") private Integer gunCount = 0; } ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/TChargingGunService.java
@@ -30,6 +30,8 @@ * @return */ PageInfo<TChargingGunVO> pageList(TChargingGunQuery query); List<TChargingGunVO> exportQRCodeList(TChargingGunQuery query); /** * 添加充电枪 ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/TChargingPileService.java
@@ -10,6 +10,8 @@ import com.ruoyi.chargingPile.dto.ChargingGunCountMonitoring; import com.ruoyi.chargingPile.dto.ChargingGunMonitoring; import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring; import com.ruoyi.chargingPile.dto.ChargingPileBatchBuildDTO; import com.ruoyi.chargingPile.dto.ChargingPileBatchBuildResultDTO; import com.ruoyi.chargingPile.dto.ChargingPileImportResultDTO; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.page.PageInfo; @@ -48,6 +50,8 @@ AjaxResult addChargingPile(TChargingPile chargingPile); ChargingPileImportResultDTO importChargingPile(MultipartFile file, Boolean cover) throws Exception; ChargingPileBatchBuildResultDTO batchBuildChargingPile(ChargingPileBatchBuildDTO dto); /** ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/TChargingGunServiceImpl.java
@@ -127,6 +127,26 @@ return pageInfo; } @Override public List<TChargingGunVO> exportQRCodeList(TChargingGunQuery query) { Set<Integer> siteIds = null; SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); Integer roleType = sysUser.getRoleType(); Integer objectId = sysUser.getObjectId(); if (roleType == 2) { siteIds = partnerService.authSite(objectId, SiteMenu.CHARGING_GUN); } else { if (!SecurityUtils.isAdmin(tokenService.getLoginUser().getUserid())) { List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData(); List<SysUserRoleVo> data2 = sysUserRoleClient.getRoleByUserId(sysUser.getUserId()).getData(); List<Integer> data1 = roleSiteClient.getSiteIds(data2.get(0).getRoleId()).getData(); data.addAll(data1); siteIds = new HashSet<>(data); } } PageInfo<TChargingGunVO> pageInfo = new PageInfo<>(1, 100000); return this.baseMapper.pageList(query, pageInfo, siteIds); } @Autowired private SiteMapper siteMapper; ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/TChargingPileServiceImpl.java
@@ -7,6 +7,7 @@ import com.ruoyi.account.api.feignClient.AppUserCarClient; import com.ruoyi.account.api.feignClient.AppUserClient; import com.ruoyi.account.api.model.TAppUserCar; import com.ruoyi.chargingPile.api.dto.TChargingGunDTO; import com.ruoyi.chargingPile.api.dto.PageChargingPileListDTO; import com.ruoyi.chargingPile.api.model.*; import com.ruoyi.chargingPile.api.query.BatchSetAccountingStrategy; @@ -17,6 +18,8 @@ import com.ruoyi.chargingPile.dto.ChargingGunCountMonitoring; import com.ruoyi.chargingPile.dto.ChargingGunMonitoring; import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring; import com.ruoyi.chargingPile.dto.ChargingPileBatchBuildDTO; import com.ruoyi.chargingPile.dto.ChargingPileBatchBuildResultDTO; import com.ruoyi.chargingPile.dto.ChargingPileImportDTO; import com.ruoyi.chargingPile.dto.ChargingPileImportErrorDTO; import com.ruoyi.chargingPile.dto.ChargingPileImportResultDTO; @@ -49,6 +52,7 @@ import com.ruoyi.system.api.feignClient.SysUserRoleClient; import com.ruoyi.system.api.model.SysUserRoleVo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -204,12 +208,12 @@ @Transactional(rollbackFor = Exception.class) public ChargingPileImportResultDTO importChargingPile(MultipartFile file, Boolean cover) throws Exception { if (file == null || file.isEmpty()) { throw new ServiceException("????????"); throw new ServiceException("请选择要导入的Excel文件"); } ExcelUtil<ChargingPileImportDTO> util = new ExcelUtil<>(ChargingPileImportDTO.class); List<ChargingPileImportDTO> rows = util.importExcel(file.getInputStream(), 0); if (rows == null || rows.isEmpty()) { throw new ServiceException("????????"); throw new ServiceException("导入文件没有数据"); } SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); @@ -234,7 +238,7 @@ List<String> errors = validateImportRow(row, siteMap, partnerNameMap, roleType, objectId, visibleSiteIds, fileCodes); String code = row == null ? null : normalizeImportValue(row.getCode()); if (!errors.isEmpty()) { result.getErrorMessage().add(new ChargingPileImportErrorDTO(rowNum, code, String.join("?", errors))); result.getErrorMessage().add(new ChargingPileImportErrorDTO(rowNum, code, String.join(";", errors))); continue; } TChargingPile chargingPile = buildImportChargingPile(row, siteMap, partnerNameMap, roleType, objectId); @@ -279,7 +283,7 @@ Set<Integer> visibleSiteIds, Set<String> fileCodes) { List<String> errors = new ArrayList<>(); if (row == null) { errors.add("??"); errors.add("数据为空"); return errors; } String siteCode = normalizeImportValue(row.getSiteCode()); @@ -287,37 +291,37 @@ String name = normalizeImportValue(row.getName()); String partnerName = normalizeImportValue(row.getPartnerName()); if (StringUtils.isEmpty(siteCode)) { errors.add("????????"); errors.add("归属电站编码不能为空"); } else if (!siteMap.containsKey(siteCode)) { errors.add("???????"); errors.add("归属电站不存在"); } if (StringUtils.isEmpty(code)) { errors.add("????????"); errors.add("设备编码不能为空"); } else if (!fileCodes.add(code)) { errors.add("???????????"); errors.add("导入文件内设备编码重复"); } if (StringUtils.isEmpty(name)) { errors.add("??????????"); errors.add("充电设备名称不能为空"); } if (row.getNumber() == null) { errors.add("??????"); errors.add("桩号不能为空"); } if (row.getType() == null) { errors.add("????????"); errors.add("设备类型不能为空"); } if (row.getRatedPower() == null) { errors.add("????????"); errors.add("额定功率不能为空"); } Site site = siteMap.get(siteCode); if (site != null) { if (visibleSiteIds != null && !visibleSiteIds.contains(site.getId())) { errors.add("??????????"); errors.add("无权操作该电站"); } Integer partnerId = resolveImportPartnerId(partnerName, site, partnerNameMap, roleType, objectId); if (partnerId == null) { errors.add("????????"); errors.add("归属合作商不存在"); } else if (Objects.equals(roleType, 2) && !Objects.equals(partnerId, objectId)) { errors.add("???????????????"); errors.add("无权导入其他合作商设备"); } } return errors; @@ -390,6 +394,74 @@ } @Override @Transactional(rollbackFor = Exception.class) public ChargingPileBatchBuildResultDTO batchBuildChargingPile(ChargingPileBatchBuildDTO dto) { if (dto == null || dto.getChargingPile() == null || dto.getChargingGun() == null) { throw new ServiceException("批量建桩参数不能为空"); } if (dto.getCodes() == null || dto.getCodes().isEmpty()) { throw new ServiceException("设备编码不能为空"); } if (dto.getNumberStart() == null) { throw new ServiceException("桩号起始值不能为空"); } if (dto.getGunCount() == null || dto.getGunCount() <= 0) { throw new ServiceException("单桩接口数必须大于0"); } List<String> codes = dto.getCodes().stream() .filter(StringUtils::isNotEmpty) .map(String::trim) .collect(Collectors.toList()); if (codes.isEmpty()) { throw new ServiceException("设备编码不能为空"); } Set<String> distinctCodes = new LinkedHashSet<>(codes); if (distinctCodes.size() != codes.size()) { throw new ServiceException("设备编码存在重复"); } ChargingPileBatchBuildResultDTO result = new ChargingPileBatchBuildResultDTO(); TChargingPile pileTemplate = dto.getChargingPile(); TChargingGunDTO gunTemplate = dto.getChargingGun(); String namePrefix = StringUtils.isEmpty(dto.getNamePrefix()) ? "" : dto.getNamePrefix().trim(); for (int i = 0; i < codes.size(); i++) { TChargingPile chargingPile = new TChargingPile(); BeanUtils.copyProperties(pileTemplate, chargingPile); chargingPile.setId(null); chargingPile.setCode(codes.get(i)); chargingPile.setNumber(dto.getNumberStart() + i); chargingPile.setName(namePrefix + chargingPile.getNumber() + "号桩"); AjaxResult pileResult = addChargingPile(chargingPile); if (pileResult.isError()) { throw new ServiceException("设备" + chargingPile.getCode() + "创建失败:" + getAjaxMessage(pileResult)); } result.setPileCount(result.getPileCount() + 1); for (int j = 1; j <= dto.getGunCount(); j++) { TChargingGunDTO chargingGun = new TChargingGunDTO(); BeanUtils.copyProperties(gunTemplate, chargingGun); String gunCode = String.format("%02d", j); chargingGun.setId(null); chargingGun.setCode(gunCode); chargingGun.setName(chargingPile.getName() + gunCode + "枪"); chargingGun.setChargingPileId(chargingPile.getId()); chargingGun.setSiteId(chargingPile.getSiteId()); chargingGun.setPartnerId(chargingPile.getPartnerId()); AjaxResult gunResult = chargingGunService.add(chargingGun); if (gunResult.isError()) { throw new ServiceException("设备" + chargingPile.getCode() + "接口" + gunCode + "创建失败:" + getAjaxMessage(gunResult)); } result.setGunCount(result.getGunCount() + 1); } } return result; } private String getAjaxMessage(AjaxResult ajaxResult) { Object msg = ajaxResult.get("msg"); return msg == null ? "未知错误" : msg.toString(); } @Override public AjaxResult addChargingPile(TChargingPile chargingPile) { AjaxResult ajaxResult = addChargingPileVerify(chargingPile); if(ajaxResult.isError()){ ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml
@@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.chargingPile.mapper.SiteMapper"> @@ -250,8 +250,8 @@ AND ts.del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()} </where> </select> <select id="getSiteListGun" resultType="com.ruoyi.chargingPile.api.model.Site"> select <include refid="Base_Column_List"></include> <select id="getSiteListGun" resultType="com.ruoyi.chargingPile.api.model.Site"> select id, `name`, partner_id from t_site <where> <if test="null != ids and ids.size()>0"> ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/TChargingGunMapper.xml
@@ -35,13 +35,25 @@ <select id="pageList" resultType="com.ruoyi.chargingPile.api.vo.TChargingGunVO"> SELECT tcg.*, tas.name AS strategyName tas.name AS strategyName, ts.name AS siteName, tp.name AS partnerName, tcp.code AS chargingPileCode FROM t_charging_gun tcg LEFT JOIN t_accounting_strategy tas ON tas.id = tcg.accounting_strategy_id LEFT JOIN t_site ts ON ts.id = tcg.site_id LEFT JOIN t_partner tp ON tp.id = tcg.partner_id LEFT JOIN t_charging_pile tcp ON tcp.id = tcg.charging_pile_id <where> <if test="query.siteId != null"> AND tcg.site_id = #{query.siteId} </if> <if test="query.partnerId != null"> AND tcg.partner_id = #{query.partnerId} </if> <if test="query.siteType != null"> AND ts.site_type = #{query.siteType} </if> <if test="query.chargingPileId != null"> AND tcg.charging_pile_id = #{query.chargingPileId} </if>