| | |
| | | @ApiModelProperty(value = "建设场所(0=其他,1=居民区,2=公共机构,3=企事业单位,4=写字楼,5=工业园区,6=交通枢纽,7=大型文体设施,8=城市绿地,9=大型建筑配建停车场,10=路边停车位,11=城际高速服务区),12=风景区,13=公交场站,14=加油加气站,15=出租车", required = true) |
| | | private Integer constructionSite; |
| | | /** |
| | | * 变压器功率(kW) |
| | | */ |
| | | @TableField("transformer_power") |
| | | @ApiModelProperty(value = "变压器功率(kW)", required = true) |
| | | private BigDecimal transformerPower; |
| | | /** |
| | | * 是否参与会员折扣(0=否,1=是) |
| | | */ |
| | | @TableField("member_discount_flag") |
| | | @ApiModelProperty(value = "是否参与会员折扣(0=否,1=是)", required = true) |
| | | private Integer memberDiscountFlag; |
| | | /** |
| | | * 站点环境图 |
| | | */ |
| | | @TableField("img_url") |
| | |
| | | private Integer chargingPileId; |
| | | @ApiModelProperty(value = "站点id") |
| | | private Integer siteId; |
| | | @ApiModelProperty(value = "是否参与会员折扣(0=否,1=是)") |
| | | private Integer memberDiscountFlag; |
| | | @ApiModelProperty(value = "桩编号") |
| | | private String number; |
| | | @ApiModelProperty(value = "充电枪id") |
| | |
| | | } |
| | | |
| | | @NotBlank(message = "问题标题不能为空") |
| | | @Size(min = 0, max = 500, message = "问题标题不能超过500个字符") |
| | | @Size(max = 50, message = "问题标题不能超过50个字符") |
| | | public String getQuestion() { |
| | | return question; |
| | | } |
| | |
| | | import java.io.IOException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.Set; |
| | | import java.util.UUID; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | |
| | | @RestController |
| | | @RequestMapping("/file/") |
| | | public class FileController { |
| | | |
| | | private static final String MANAGE_MEDIA_EXTENSIONS = "mp4,avi,rmvb,mov,wmv,flv,mkv,mpeg,mpg"; |
| | | |
| | | @Autowired |
| | | private FileUploadConfig fileUploadConfig; |
| | |
| | | // 获取文件后缀 |
| | | String ext = filename.substring(filename.lastIndexOf(".") + 1); |
| | | // 检查文件类型 |
| | | String allowedExt = getAllowedExt(fileUploadConfig.getDocExt()); |
| | | String allowedExt = getManageAllowedExt(); |
| | | if (!isAllowedExt(ext, allowedExt)) { |
| | | return invalidExtResult(allowedExt); |
| | | } |
| | |
| | | return fileUploadConfig.getAllowExt(); |
| | | } |
| | | |
| | | private boolean isAllowedExt(String ext, String allowedExt) { |
| | | String getManageAllowedExt() { |
| | | return mergeAllowedExt(getAllowedExt(fileUploadConfig.getDocExt()), MANAGE_MEDIA_EXTENSIONS); |
| | | } |
| | | |
| | | static String mergeAllowedExt(String... extensionGroups) { |
| | | Set<String> extensions = new LinkedHashSet<>(); |
| | | if (extensionGroups != null) { |
| | | for (String group : extensionGroups) { |
| | | if (group == null) { |
| | | continue; |
| | | } |
| | | for (String item : group.split(",")) { |
| | | String normalizedItem = item.trim(); |
| | | if (normalizedItem.startsWith(".")) { |
| | | normalizedItem = normalizedItem.substring(1); |
| | | } |
| | | if (!normalizedItem.isEmpty()) { |
| | | extensions.add(normalizedItem.toLowerCase()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return String.join(",", extensions); |
| | | } |
| | | |
| | | static boolean isAllowedExt(String ext, String allowedExt) { |
| | | if (ext == null || allowedExt == null || allowedExt.trim().length() == 0) { |
| | | return false; |
| | | } |
| | |
| | | private AjaxResult invalidExtResult(String allowedExt) { |
| | | return AjaxResult.error("上传文件格式不正确,仅支持" + (allowedExt == null ? "" : allowedExt)); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.account.controller; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertFalse; |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | |
| | | class FileControllerTest { |
| | | |
| | | @Test |
| | | void managementUploadAllowsConfiguredDocumentsAndFaqVideos() { |
| | | String allowed = FileController.mergeAllowedExt("png,jpg,pdf,doc,docx", "mp4,avi,rmvb,mov"); |
| | | |
| | | assertTrue(FileController.isAllowedExt("pdf", allowed)); |
| | | assertTrue(FileController.isAllowedExt("MP4", allowed)); |
| | | assertTrue(FileController.isAllowedExt(".mov", allowed)); |
| | | assertFalse(FileController.isAllowedExt("jsp", allowed)); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.chargingPile.api.vo.*; |
| | | import com.ruoyi.chargingPile.api.domain.SiteMenu; |
| | | import com.ruoyi.chargingPile.dto.ChargingPercentBack; |
| | | import com.ruoyi.chargingPile.dto.BatchSetSiteAccountingStrategyDTO; |
| | | import com.ruoyi.chargingPile.dto.SiteExcelDTO; |
| | | import com.ruoyi.chargingPile.dto.SiteImportResultDTO; |
| | | import com.ruoyi.common.core.dto.ChargingOrderGroup; |
| | | import com.ruoyi.common.core.dto.ChargingPercentProvinceDto; |
| | | import com.ruoyi.chargingPile.service.*; |
| | |
| | | import com.ruoyi.common.core.utils.DateUtils; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.poi.ExcelUtil; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import com.ruoyi.common.log.annotation.Log; |
| | |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalTime; |
| | | import java.time.temporal.ChronoUnit; |
| | |
| | | SiteInfoVO siteInfoVO = new SiteInfoVO(); |
| | | TChargingGun chargingGun = chargingGunService.getOne(new LambdaQueryWrapper<TChargingGun>().eq(TChargingGun::getFullNumber, number)); |
| | | Site site = siteService.getById(chargingGun.getSiteId()); |
| | | siteInfoVO.setMemberDiscountFlag(site.getMemberDiscountFlag()); |
| | | Integer accountingStrategyId = chargingGun.getAccountingStrategyId(); |
| | | if(null == accountingStrategyId){ |
| | | accountingStrategyId = site.getAccountingStrategyId(); |
| | |
| | | if(DateUtils.string2LocalTime(tAccountingStrategyDetail.getStartTime() + ":00").compareTo(LocalTime.now()) <= 0 |
| | | && DateUtils.string2LocalTime(tAccountingStrategyDetail.getEndTime() + ("23:59:59".equals(tAccountingStrategyDetail.getEndTime())?"":":00")).compareTo(LocalTime.now()) > 0){ |
| | | siteInfoVO.setElectrovalence(tAccountingStrategyDetail.getElectrovalence().add(tAccountingStrategyDetail.getServiceCharge()).setScale(4, BigDecimal.ROUND_HALF_UP)); |
| | | siteInfoVO.setVipElectrovalence(siteInfoVO.getElectrovalence()); |
| | | // 获取当前登录用户id |
| | | String token = SecurityUtils.getToken(ServletUtils.getRequest()); |
| | | if(StringUtils.isNotEmpty(token)){ |
| | | if(Integer.valueOf(1).equals(site.getMemberDiscountFlag()) && StringUtils.isNotEmpty(token)){ |
| | | Long userId = tokenService.getLoginUserApplet().getUserId(); |
| | | // 根据id查询用户信息 |
| | | TAppUser appUser = appUserClient.getUserById(userId).getData(); |
| | |
| | | TChargingPile one = chargingPileService.lambdaQuery().eq(TChargingPile::getCode, number).one(); |
| | | TChargingGun chargingGun = chargingGunService.getOne(new LambdaQueryWrapper<TChargingGun>().eq(TChargingGun::getChargingPileId, one.getId()).last(" limit 0, 1")); |
| | | Site site = siteService.getById(chargingGun.getSiteId()); |
| | | siteInfoVO.setMemberDiscountFlag(site.getMemberDiscountFlag()); |
| | | Integer accountingStrategyId = chargingGun.getAccountingStrategyId(); |
| | | if(null == accountingStrategyId){ |
| | | accountingStrategyId = site.getAccountingStrategyId(); |
| | |
| | | if(DateUtils.string2LocalTime(tAccountingStrategyDetail.getStartTime() + ":00").compareTo(LocalTime.now()) <= 0 |
| | | && DateUtils.string2LocalTime(tAccountingStrategyDetail.getEndTime() + ("23:59:59".equals(tAccountingStrategyDetail.getEndTime())?"":":00")).compareTo(LocalTime.now()) > 0){ |
| | | siteInfoVO.setElectrovalence(tAccountingStrategyDetail.getElectrovalence().add(tAccountingStrategyDetail.getServiceCharge()).setScale(4, BigDecimal.ROUND_HALF_UP)); |
| | | siteInfoVO.setVipElectrovalence(siteInfoVO.getElectrovalence()); |
| | | // 获取当前登录用户id |
| | | String token = SecurityUtils.getToken(ServletUtils.getRequest()); |
| | | if(StringUtils.isNotEmpty(token)){ |
| | | if(Integer.valueOf(1).equals(site.getMemberDiscountFlag()) && StringUtils.isNotEmpty(token)){ |
| | | Long userId = tokenService.getLoginUserApplet().getUserId(); |
| | | // 根据id查询用户信息 |
| | | TAppUser appUser = appUserClient.getUserById(userId).getData(); |
| | |
| | | } |
| | | } |
| | | |
| | | if(Objects.nonNull(byId1.getDiscount())){ |
| | | if(Integer.valueOf(1).equals(site.getMemberDiscountFlag()) && Objects.nonNull(byId1.getDiscount())){ |
| | | siteInfoVO.setVipElectrovalence((tAccountingStrategyDetail.getServiceCharge().multiply(byId1.getDiscount()).add(tAccountingStrategyDetail.getElectrovalence())).setScale(4, BigDecimal.ROUND_HALF_UP)); |
| | | }else { |
| | | siteInfoVO.setVipElectrovalence((tAccountingStrategyDetail.getServiceCharge().add(tAccountingStrategyDetail.getElectrovalence())).setScale(4, BigDecimal.ROUND_HALF_UP)); |
| | |
| | | public AjaxResult<Map<String,ChargingGunCountVO>> getChargingGunCount1(@RequestParam(value = "siteId")@ApiParam(value = "站点id", required = true)Integer siteId){ |
| | | return AjaxResult.success(chargingGunService.getChargingGunCount(siteId)); |
| | | } |
| | | |
| | | @RequiresPermissions(value = {"/site/add"}, logical = Logical.OR) |
| | | @GetMapping("/importTemplate") |
| | | @ApiOperation(value = "下载站点导入模板", tags = {"管理后台-站点管理"}) |
| | | public void importTemplate(HttpServletResponse response) { |
| | | ExcelUtil<SiteExcelDTO> util = new ExcelUtil<>(SiteExcelDTO.class); |
| | | util.importTemplateExcel(response, "站点导入模板", |
| | | "填写说明:所在区域填写区县行政区划代码;带*字段为必填;合作商、服务商、场地方和计费模板填写系统中的完整名称。") ; |
| | | } |
| | | |
| | | @RequiresPermissions(value = {"/site/add"}, logical = Logical.OR) |
| | | @PostMapping("/import") |
| | | @Log(title = "【站点管理】导入站点", businessType = BusinessType.IMPORT, operatorType = OperatorType.MANAGE) |
| | | @ApiOperation(value = "导入站点", tags = {"管理后台-站点管理"}) |
| | | public AjaxResult importSites(@RequestParam("file") MultipartFile file, |
| | | @RequestParam(value = "cover", defaultValue = "false") Boolean cover) throws Exception { |
| | | SiteImportResultDTO result = siteService.importSites(file, cover); |
| | | if (Boolean.TRUE.equals(result.getNeedConfirm())) { |
| | | return AjaxResult.error("发现重复站点编号,请确认是否覆盖", result); |
| | | } |
| | | if (result.getErrorCount() != null && result.getErrorCount() > 0) { |
| | | return AjaxResult.error("导入失败,请检查错误信息", result); |
| | | } |
| | | return AjaxResult.success("导入成功", result); |
| | | } |
| | | |
| | | @RequiresPermissions(value = {"/site/export", "/site"}, logical = Logical.OR) |
| | | @PutMapping("/export") |
| | | @Log(title = "【站点管理】导出站点", businessType = BusinessType.EXPORT, operatorType = OperatorType.MANAGE) |
| | | @ApiOperation(value = "导出站点", tags = {"管理后台-站点管理"}) |
| | | public void exportSites(@RequestBody GetSiteList query, HttpServletResponse response) { |
| | | List<Site> sites = siteService.listSitesForExport(query); |
| | | Set<Integer> partnerIds = sites.stream() |
| | | .flatMap(site -> Arrays.asList(site.getPartnerId(), site.getVenuePartnerId()).stream()) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Integer, String> partnerNames = partnerIds.isEmpty() ? Collections.emptyMap() |
| | | : partnerService.listByIds(partnerIds).stream() |
| | | .collect(Collectors.toMap(Partner::getId, Partner::getName, (a, b) -> a)); |
| | | Set<Integer> strategyIds = sites.stream().map(Site::getAccountingStrategyId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Integer, String> strategyNames = strategyIds.isEmpty() ? Collections.emptyMap() |
| | | : accountingStrategyService.listByIds(strategyIds).stream() |
| | | .collect(Collectors.toMap(TAccountingStrategy::getId, TAccountingStrategy::getName, (a, b) -> a)); |
| | | List<SiteExcelDTO> rows = sites.stream().map(site -> SiteExcelDTO.from(site, |
| | | partnerNames.get(site.getPartnerId()), partnerNames.get(site.getVenuePartnerId()), |
| | | strategyNames.get(site.getAccountingStrategyId()))).collect(Collectors.toList()); |
| | | new ExcelUtil<>(SiteExcelDTO.class).exportExcel(response, rows, "站点数据"); |
| | | } |
| | | |
| | | @RequiresPermissions(value = {"/site/add"}, logical = Logical.OR) |
| | | @ResponseBody |
| | |
| | | @PostMapping("/batchSetAccountingStrategy") |
| | | @ApiOperation(value = "批量设置站点计费策略", tags = {"管理后台-站点管理"}) |
| | | @Log(title = "【站点管理】批量设置站点计费策略", businessType = BusinessType.UPDATE) |
| | | public AjaxResult batchSetAccountingStrategy(@RequestParam("ids") List<Integer> ids, @RequestParam("accountingStrategyId") Integer accountingStrategyId){ |
| | | siteService.batchSetAccountingStrategy(ids, accountingStrategyId); |
| | | public AjaxResult batchSetAccountingStrategy(@RequestBody BatchSetSiteAccountingStrategyDTO request){ |
| | | if (request == null || CollectionUtils.isEmpty(request.getIds())) { |
| | | return AjaxResult.error("请选择要设置计费策略的站点"); |
| | | } |
| | | if (request.getAccountingStrategyId() == null) { |
| | | return AjaxResult.error("请选择计费模板"); |
| | | } |
| | | siteService.batchSetAccountingStrategy(request.getIds(), request.getAccountingStrategyId()); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | |
| | | import io.swagger.annotations.ApiImplicitParams; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | @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 -> { |
| | | ChargingPileExport export = new ChargingPileExport(); |
| | | BeanUtils.copyProperties(record, export); |
| | | return export; |
| | | }).collect(Collectors.toList()); |
| | | ExcelUtil<ChargingPileExport> util = new ExcelUtil<>(ChargingPileExport.class); |
| | | List<PageChargingPileListDTO> records = chargingPileService.listChargingPileForExport(page); |
| | | Set<Integer> pileIds = records.stream() |
| | | .map(PageChargingPileListDTO::getId) |
| | | .filter(id -> id != null) |
| | | .collect(Collectors.toSet()); |
| | | Map<Integer, TChargingPile> pileMap = pileIds.isEmpty() |
| | | ? Collections.emptyMap() |
| | | : chargingPileService.listByIds(pileIds).stream() |
| | | .collect(Collectors.toMap(TChargingPile::getId, item -> item, (left, right) -> left)); |
| | | List<TChargingGunVO> guns = pileIds.isEmpty() |
| | | ? Collections.emptyList() |
| | | : chargingGunService.exportQRCodeList(new TChargingGunQuery()).stream() |
| | | .filter(gun -> pileIds.contains(gun.getChargingPileId())) |
| | | .collect(Collectors.toList()); |
| | | Map<Integer, List<TChargingGunVO>> gunsByPile = guns.stream() |
| | | .collect(Collectors.groupingBy(TChargingGunVO::getChargingPileId)); |
| | | List<ChargingPileExport> exports = new ArrayList<>(); |
| | | for (PageChargingPileListDTO record : records) { |
| | | List<TChargingGunVO> pileGuns = gunsByPile.getOrDefault(record.getId(), Collections.emptyList()); |
| | | if (pileGuns.isEmpty()) { |
| | | exports.add(ChargingPileExport.from(record, pileMap.get(record.getId()), null)); |
| | | continue; |
| | | } |
| | | for (TChargingGunVO gun : pileGuns) { |
| | | exports.add(ChargingPileExport.from(record, pileMap.get(record.getId()), gun)); |
| | | } |
| | | } |
| | | ExcelUtil<ChargingPileExport> util = new ExcelUtil<>(ChargingPileExport.class); |
| | | util.exportExcel(response, exports, "充电桩数据"); |
| | | } |
| | | |
| New file |
| | |
| | | package com.ruoyi.chargingPile.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class BatchSetSiteAccountingStrategyDTO { |
| | | |
| | | private List<Integer> ids; |
| | | |
| | | private Integer accountingStrategyId; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.dto; |
| | | |
| | | import com.ruoyi.chargingPile.api.model.Site; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import lombok.Data; |
| | | import org.springframework.beans.BeanUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.format.DateTimeFormatter; |
| | | |
| | | @Data |
| | | public class SiteExcelDTO { |
| | | |
| | | @Excel(name = "*归属合作商/服务商", width = 28) |
| | | private String partnerName; |
| | | @Excel(name = "场地方", width = 25) |
| | | private String venuePartnerName; |
| | | @Excel(name = "*站点编号", width = 22) |
| | | private String code; |
| | | @Excel(name = "*站点名称", width = 30) |
| | | private String name; |
| | | @Excel(name = "*站点类型", width = 20, readConverterExp = "0=其他,1=公共,2=个人,3=公交(专用),4=环卫(专用),5=物流(专用),6=出租车(专用)") |
| | | private Integer siteType; |
| | | @Excel(name = "*经营类别", width = 15, readConverterExp = "1=直营,2=非直营") |
| | | private Integer businessCategory; |
| | | @Excel(name = "*站点状态", width = 18, readConverterExp = "1=正常使用,2=维护中,3=关闭下线") |
| | | private Integer status; |
| | | @Excel(name = "*建设场所", width = 24, readConverterExp = "0=其他,1=居民区,2=公共机构,3=企事业单位,4=写字楼,5=工业园区,6=交通枢纽,7=大型文体设施,8=城市绿地,9=大型建筑配建停车场,10=路边停车位,11=城际高速服务区,12=风景区,13=公交场站,14=加油加气站,15=出租车") |
| | | private Integer constructionSite; |
| | | @Excel(name = "*变压器功率", width = 18) |
| | | private BigDecimal transformerPower; |
| | | @Excel(name = "*是否参与会员折扣", width = 22, readConverterExp = "0=否,1=是") |
| | | private Integer memberDiscountFlag; |
| | | @Excel(name = "*所在区域", width = 20, prompt = "填写区县行政区划代码,如510903") |
| | | private String areaCode; |
| | | @Excel(name = "*详细地址", width = 35) |
| | | private String address; |
| | | @Excel(name = "*经度", width = 18) |
| | | private String lon; |
| | | @Excel(name = "*纬度", width = 18) |
| | | private String lat; |
| | | @Excel(name = "*充电站国家代码", width = 20) |
| | | private String countryCode; |
| | | @Excel(name = "*站点电话", width = 22) |
| | | private String phone; |
| | | @Excel(name = "*服务电话", width = 22) |
| | | private String servicePhone; |
| | | @Excel(name = "站点引导", width = 30) |
| | | private String guide; |
| | | @Excel(name = "运营时间", width = 30) |
| | | private String startServiceTime; |
| | | @Excel(name = "站内提供服务", width = 30) |
| | | private String serviceDescription; |
| | | @Excel(name = "车型描述", width = 30) |
| | | private String vehicleDescription; |
| | | @Excel(name = "*车位数量", width = 15) |
| | | private Integer parkingSpace; |
| | | @Excel(name = "*充电电费描述", width = 35) |
| | | private String rateDescription; |
| | | @Excel(name = "超时占位费描述", width = 35) |
| | | private String spaceChargeExplain; |
| | | @Excel(name = "计费模板", width = 25) |
| | | private String accountingStrategyName; |
| | | @Excel(name = "建站时间", width = 22, prompt = "格式:yyyy-MM-dd HH:mm:ss") |
| | | private String establishmentTime; |
| | | @Excel(name = "*排序", width = 12) |
| | | private Integer sort; |
| | | @Excel(name = "备注", width = 30) |
| | | private String remark; |
| | | @Excel(name = "*电站分类", width = 22, readConverterExp = "1=充电站,2=换电站,3=充换电一体站") |
| | | private Integer stationClassification; |
| | | @Excel(name = "通用类型", width = 18, readConverterExp = "1=通用类型,2=非通用") |
| | | private Integer generalApplicationType; |
| | | @Excel(name = "充换电站所在县以下行政区划代码", width = 34) |
| | | private String areaCodeCountryside; |
| | | @Excel(name = "*7*24小时营业", width = 20, readConverterExp = "0=否,1=是") |
| | | private Integer roundTheClock; |
| | | @Excel(name = "*停车费类型", width = 22, readConverterExp = "0=免费,1=不免费,2=限时免费停车,3=充电限时减免,4=参考场地实际收费标准") |
| | | private Integer parkType; |
| | | @Excel(name = "停车费描述", width = 35) |
| | | private String parkFee; |
| | | @Excel(name = "服务车型描述", width = 30) |
| | | private String swapMatchCars; |
| | | @Excel(name = "支付方式", width = 18, readConverterExp = "1=刷卡,2=线上,3=现金") |
| | | private Integer payment; |
| | | @Excel(name = "是否支持预约", width = 20, readConverterExp = "1=支持,2=不支持") |
| | | private Integer supportOrder; |
| | | @Excel(name = "*电费类型", width = 22, readConverterExp = "0=其他用电,1=商业用电,2=普通工业用电,3=大工业用电") |
| | | private Integer electricityType; |
| | | @Excel(name = "*报装类型", width = 20, readConverterExp = "1=独立报装,2=非独立报装") |
| | | private Integer businessExpandType; |
| | | @Excel(name = "*报装电源容量", width = 20) |
| | | private BigDecimal capacity; |
| | | @Excel(name = "*站点额定总功率", width = 22) |
| | | private BigDecimal ratedPower; |
| | | @Excel(name = "*是否峰谷分时", width = 20, readConverterExp = "0=否,1=是") |
| | | private Integer periodFee; |
| | | @Excel(name = "*正式投运时间", width = 22, prompt = "格式:yyyy-MM-dd") |
| | | private String officialRunTime; |
| | | @Excel(name = "充换电站方位", width = 24, readConverterExp = "1=地面-停车场,2=地面-路侧,3=地下停车,4=立体式停车楼") |
| | | private Integer stationOrientation; |
| | | @Excel(name = "充换电站建设面积", width = 24) |
| | | private BigDecimal stationArea; |
| | | @Excel(name = "充换电站人工值守", width = 24, readConverterExp = "0=无,1=有") |
| | | private Integer havePerson; |
| | | @Excel(name = "*视频监控配套情况", width = 24, readConverterExp = "0=无,1=有") |
| | | private Integer videoMonitor; |
| | | @Excel(name = "周边配套设施", width = 30) |
| | | private String supportingFacilities; |
| | | @Excel(name = "是否有小票机", width = 20, readConverterExp = "0=无,1=有") |
| | | private Integer printerFlag; |
| | | @Excel(name = "是否有道闸", width = 18, readConverterExp = "0=无,1=有") |
| | | private Integer barrierFlag; |
| | | @Excel(name = "是否有地锁", width = 18, readConverterExp = "0=无,1=有") |
| | | private Integer parkingLockFlag; |
| | | @Excel(name = "*设备所属方名称", width = 28) |
| | | private String equipmentOwnerName; |
| | | @Excel(name = "*供电局用户编号", width = 25) |
| | | private String residentNo; |
| | | @Excel(name = "*供电类型", width = 18, readConverterExp = "1=直供电,2=转供电") |
| | | private Integer supplyType; |
| | | @Excel(name = "*表号", width = 20) |
| | | private String wattHourMeterNo; |
| | | @Excel(name = "*外电功率", width = 18) |
| | | private BigDecimal forwardPower; |
| | | @Excel(name = "*服务费描述", width = 35) |
| | | private String serviceFee; |
| | | @Excel(name = "*充电站全省唯一备案号", width = 32) |
| | | private String recordUniqueNo; |
| | | |
| | | public static SiteExcelDTO from(Site site, String partnerName, String venuePartnerName, String strategyName) { |
| | | SiteExcelDTO dto = new SiteExcelDTO(); |
| | | BeanUtils.copyProperties(site, dto); |
| | | dto.setPartnerName(partnerName); |
| | | dto.setVenuePartnerName(venuePartnerName); |
| | | dto.setAccountingStrategyName(strategyName); |
| | | dto.setAreaCode(site.getDistrictsCode()); |
| | | if (site.getEstablishmentTime() != null) { |
| | | dto.setEstablishmentTime(site.getEstablishmentTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); |
| | | } |
| | | return dto; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class SiteImportErrorDTO { |
| | | private Integer rowNum; |
| | | private String code; |
| | | private String reason; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class SiteImportResultDTO { |
| | | private Integer successCount = 0; |
| | | private Integer updateCount = 0; |
| | | private Integer errorCount = 0; |
| | | private Boolean needConfirm = false; |
| | | private List<String> duplicateCodes = new ArrayList<>(); |
| | | private List<SiteImportErrorDTO> errorMessage = new ArrayList<>(); |
| | | } |
| | |
| | | package com.ruoyi.chargingPile.export; |
| | | |
| | | import com.ruoyi.chargingPile.api.dto.PageChargingPileListDTO; |
| | | import com.ruoyi.chargingPile.api.model.TChargingPile; |
| | | import com.ruoyi.chargingPile.api.vo.TChargingGunVO; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import lombok.Data; |
| | | |
| | |
| | | |
| | | @Data |
| | | public class ChargingPileExport { |
| | | @Excel(name = "设备编码", width = 25) |
| | | @Excel(name = "*设备编码", width = 25) |
| | | private String code; |
| | | @Excel(name = "充电设备名称", width = 25) |
| | | @Excel(name = "*设备名称", width = 25) |
| | | private String name; |
| | | @Excel(name = "桩号", width = 10) |
| | | @Excel(name = "*桩号", width = 10) |
| | | private String number; |
| | | @Excel(name = "\u8bbe\u5907\u7c7b\u578b", width = 20, readConverterExp = "0=\u5176\u4ed6,1=\u76f4\u6d41\u8bbe\u5907,2=\u4ea4\u6d41\u8bbe\u5907,3=\u4ea4\u76f4\u6d41\u4e00\u4f53\u8bbe\u5907,4=\u65e0\u7ebf\u8bbe\u5907,5=\u6709\u5e8f") |
| | | @Excel(name = "设备类型", width = 20, readConverterExp = "0=其他,1=直流设备,2=交流设备,3=交直流一体设备,4=无线设备,5=有序,6=V2G") |
| | | private Integer type; |
| | | @Excel(name = "归属电站", width = 30) |
| | | private String siteName; |
| | | @Excel(name = "归属合作商", width = 30) |
| | | @Excel(name = "设备合作商", width = 30) |
| | | private String partnerName; |
| | | @Excel(name = "生产机构代码", width = 25) |
| | | private String manufacturerCode; |
| | | @Excel(name = "生产商名称", width = 30) |
| | | private String manufacturer; |
| | | @Excel(name = "设备型号", width = 25) |
| | | private String equipmentType; |
| | | @Excel(name = "额定功率", width = 15) |
| | | @Excel(name = "设备生产日期", width = 15) |
| | | private String productionDate; |
| | | @Excel(name = "*额定功率", width = 15) |
| | | private BigDecimal ratedPower; |
| | | @Excel(name = "设备充电站编号", width = 25) |
| | | private String chargingStationNumber; |
| | | @Excel(name = "设备唯一编码", width = 25) |
| | | private String equipmentUniqueNumber; |
| | | @Excel(name = "*设备分类", width = 30, readConverterExp = "1=车辆充电设备,2=换电站内的电池箱充电设备") |
| | | private Integer equipmentClassification; |
| | | @Excel(name = "设备经度", width = 20) |
| | | private String equipmentLng; |
| | | @Excel(name = "设备纬度", width = 20) |
| | | private String equipmentLat; |
| | | |
| | | @Excel(name = "*接口编码", width = 20) |
| | | private String interfaceCode; |
| | | @Excel(name = "*接口名称", width = 25) |
| | | private String interfaceName; |
| | | @Excel(name = "*设备接口分类", width = 30, readConverterExp = "1=车辆充电设备接口,2=换电站内的电池箱充电设备接口") |
| | | private Integer interfaceEquipmentClassification; |
| | | @Excel(name = "*接口类型", width = 35, readConverterExp = "0=其他,1=家用插座(模式2),2=交流接口插座(模式3,连接方式B),3=交流接口插头(带枪线,模式3,连接方式C),4=直流接口枪头(带枪线,模式4),5=无线充电座") |
| | | private Integer interfaceType; |
| | | @Excel(name = "*接口状态", width = 20, readConverterExp = "1=离线,2=空闲,3=占用(未充电),4=占用(充电中),5=占用(已充满),6=占用(预约锁定),7=故障") |
| | | private Integer interfaceStatus; |
| | | @Excel(name = "*充电方式", width = 15, readConverterExp = "1=超级快充,2=快充,3=慢充") |
| | | private Integer chargeMode; |
| | | @Excel(name = "计费策略", width = 30) |
| | | private String strategyName; |
| | | @Excel(name = "*额定电压上限", width = 18) |
| | | private BigDecimal upperRatedVoltage; |
| | | @Excel(name = "*额定电压下限", width = 18) |
| | | private BigDecimal lowerLimitOfRatedVoltage; |
| | | @Excel(name = "恒功率电压上限", width = 18) |
| | | private BigDecimal constantVoltageUpperLimits; |
| | | @Excel(name = "恒功率电压下限", width = 18) |
| | | private BigDecimal constantVoltageLowerLimits; |
| | | @Excel(name = "*额定电流", width = 15) |
| | | private BigDecimal ratedCurrent; |
| | | @Excel(name = "恒功率电流上限", width = 18) |
| | | private BigDecimal constantCurrentUpperLimits; |
| | | @Excel(name = "恒功率电流下限", width = 18) |
| | | private BigDecimal constantCurrentLowerLimits; |
| | | @Excel(name = "*额定功率", width = 15) |
| | | private BigDecimal interfaceRatedPower; |
| | | @Excel(name = "车位号", width = 15) |
| | | private String parkingNumber; |
| | | @Excel(name = "车位状态", width = 15, readConverterExp = "0=未知,1=空闲,2=占用") |
| | | private Integer parkingStatus; |
| | | @Excel(name = "地锁状态", width = 15, readConverterExp = "0=未知,1=已解锁,2=已上锁") |
| | | private Integer parkingLockState; |
| | | @Excel(name = "*国家标准", width = 20, readConverterExp = "1=2011,2=2015,3=兼容2011和2015") |
| | | private String nationalStandard; |
| | | @Excel(name = "接口说明", width = 30) |
| | | private String remark; |
| | | @Excel(name = "辅助电源", width = 20, readConverterExp = "1=12V,2=24V,3=兼容12V和24V") |
| | | private Integer auxPower; |
| | | @Excel(name = "*运营状态", width = 18, readConverterExp = "0=未知,1=建设中,5=关闭下线,6=维护中,50=正常使用") |
| | | private Integer operateStatus; |
| | | @Excel(name = "运营时间", width = 25) |
| | | private String operateHours; |
| | | |
| | | public static ChargingPileExport from(PageChargingPileListDTO record, TChargingPile pile, TChargingGunVO gun) { |
| | | ChargingPileExport export = new ChargingPileExport(); |
| | | if (pile != null) { |
| | | export.setCode(pile.getCode()); |
| | | export.setName(pile.getName()); |
| | | export.setNumber(pile.getNumber() == null ? null : String.valueOf(pile.getNumber())); |
| | | export.setType(pile.getType()); |
| | | export.setManufacturerCode(pile.getManufacturerCode()); |
| | | export.setManufacturer(pile.getManufacturer()); |
| | | export.setEquipmentType(pile.getEquipmentType()); |
| | | export.setProductionDate(pile.getProductionDate() == null ? null : pile.getProductionDate().toString()); |
| | | export.setRatedPower(pile.getRatedPower()); |
| | | export.setChargingStationNumber(pile.getChargingStationNumber()); |
| | | export.setEquipmentUniqueNumber(pile.getEquipmentUniqueNumber()); |
| | | export.setEquipmentClassification(pile.getEquipmentClassification()); |
| | | export.setEquipmentLng(pile.getEquipmentLng()); |
| | | export.setEquipmentLat(pile.getEquipmentLat()); |
| | | } else if (record != null) { |
| | | export.setCode(record.getCode()); |
| | | export.setName(record.getName()); |
| | | export.setNumber(record.getNumber()); |
| | | export.setType(record.getType()); |
| | | export.setManufacturer(record.getManufacturer()); |
| | | export.setEquipmentType(record.getEquipmentType()); |
| | | export.setRatedPower(record.getRatedPower()); |
| | | } |
| | | if (record != null) { |
| | | export.setSiteName(record.getSiteName()); |
| | | export.setPartnerName(record.getPartnerName()); |
| | | } |
| | | if (gun != null) { |
| | | export.setInterfaceCode(gun.getCode()); |
| | | export.setInterfaceName(gun.getName()); |
| | | export.setInterfaceEquipmentClassification(gun.getEquipmentClassification()); |
| | | export.setInterfaceType(gun.getType()); |
| | | export.setInterfaceStatus(gun.getStatus()); |
| | | export.setChargeMode(gun.getChargeMode()); |
| | | export.setStrategyName(gun.getStrategyName()); |
| | | export.setUpperRatedVoltage(gun.getUpperRatedVoltage()); |
| | | export.setLowerLimitOfRatedVoltage(gun.getLowerLimitOfRatedVoltage()); |
| | | export.setConstantVoltageUpperLimits(gun.getConstantVoltageUpperLimits()); |
| | | export.setConstantVoltageLowerLimits(gun.getConstantVoltageLowerLimits()); |
| | | export.setRatedCurrent(gun.getRatedCurrent()); |
| | | export.setConstantCurrentUpperLimits(gun.getConstantCurrentUpperLimits()); |
| | | export.setConstantCurrentLowerLimits(gun.getConstantCurrentLowerLimits()); |
| | | export.setInterfaceRatedPower(gun.getRatedPower()); |
| | | export.setParkingNumber(gun.getParkingNumber()); |
| | | export.setParkingStatus(gun.getParkingStatus()); |
| | | export.setParkingLockState(gun.getParkingLockState()); |
| | | export.setNationalStandard(gun.getNationalStandard()); |
| | | export.setRemark(gun.getRemark()); |
| | | export.setAuxPower(gun.getAuxPower()); |
| | | export.setOperateStatus(gun.getOpreateStatus()); |
| | | export.setOperateHours(gun.getOpreateHours()); |
| | | } |
| | | return export; |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.chargingPile.api.query.SiteQuery; |
| | | import com.ruoyi.chargingPile.api.vo.SiteDetailVO; |
| | | import com.ruoyi.chargingPile.api.vo.SiteVO; |
| | | import com.ruoyi.chargingPile.dto.SiteImportResultDTO; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | */ |
| | | PageInfo<GetSiteListDTO> getSiteList(GetSiteList siteList); |
| | | PageInfo<GetSiteListDTO> getSiteList1(Long userId); |
| | | List<Site> listSitesForExport(GetSiteList siteList); |
| | | SiteImportResultDTO importSites(MultipartFile file, Boolean cover) throws Exception; |
| | | |
| | | |
| | | /** |
| | |
| | | package com.ruoyi.chargingPile.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.chargingPile.api.dto.GetPermissionConfigurationDTO; |
| | | import com.ruoyi.chargingPile.api.dto.PartnerListDTO; |
| | |
| | | |
| | | //修改数据 |
| | | this.updateById(partner); |
| | | updateShareRatios(partner); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | void updateShareRatios(Partner partner) { |
| | | this.update(new UpdateWrapper<Partner>() |
| | | .eq("id", partner.getId()) |
| | | .set("electricity_share_ratio", partner.getElectricityShareRatio()) |
| | | .set("service_share_ratio", partner.getServiceShareRatio())); |
| | | } |
| | | |
| | | |
| | | private void assignDefaultRole(Partner partner) { |
| | | String roleName = partnerTypeRoleName(partner.getPartnerType()); |
| | | Integer roleId = resolveDefaultRoleId(partner.getPartnerType()); |
| | | partner.setRoleId(roleId); |
| | | } |
| | | |
| | | Integer resolveDefaultRoleId(Integer partnerType) { |
| | | String roleName = partnerTypeRoleName(partnerType); |
| | | Integer roleId = this.baseMapper.getDefaultPartnerRoleId(roleName); |
| | | if (roleId == null && !Integer.valueOf(1).equals(partnerType)) { |
| | | roleId = this.baseMapper.getDefaultPartnerRoleId("合作商"); |
| | | } |
| | | if (roleId == null) { |
| | | throw new RuntimeException("系统内置角色【" + roleName + "】不存在,请先配置角色"); |
| | | } |
| | | partner.setRoleId(roleId); |
| | | return roleId; |
| | | } |
| | | |
| | | private String partnerTypeRoleName(Integer partnerType) { |
| | |
| | | import com.ruoyi.chargingPile.api.vo.SiteDetailVO; |
| | | import com.ruoyi.chargingPile.api.vo.SiteVO; |
| | | import com.ruoyi.chargingPile.api.domain.SiteMenu; |
| | | import com.ruoyi.chargingPile.dto.SiteExcelDTO; |
| | | import com.ruoyi.chargingPile.dto.SiteImportErrorDTO; |
| | | import com.ruoyi.chargingPile.dto.SiteImportResultDTO; |
| | | import com.ruoyi.chargingPile.mapper.SiteMapper; |
| | | import com.ruoyi.chargingPile.service.*; |
| | | import com.ruoyi.chargingPile.util.UUIDUtil; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.exception.ServiceException; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.poi.ExcelUtil; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import com.ruoyi.common.security.service.TokenService; |
| | |
| | | import com.ruoyi.integration.api.elutong.model.*; |
| | | import com.ruoyi.integration.api.feignClient.*; |
| | | import com.ruoyi.other.api.domain.TVip; |
| | | import com.ruoyi.other.api.domain.Region; |
| | | import com.ruoyi.other.api.feignClient.OtherClient; |
| | | import com.ruoyi.other.api.feignClient.RoleSiteClient; |
| | | import com.ruoyi.other.api.feignClient.UserSiteClient; |
| | | import com.ruoyi.other.api.feignClient.VipClient; |
| | |
| | | import com.ruoyi.system.api.feignClient.SysUserRoleClient; |
| | | import com.ruoyi.system.api.model.SysUserRoleVo; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | |
| | | @Resource |
| | | private RoleSiteClient roleSiteClient; |
| | | @Resource |
| | | private OtherClient otherClient; |
| | | @Resource |
| | | private TokenService tokenService; |
| | | @Resource |
| | | private AppUserClient appUserClient; |
| | |
| | | |
| | | @Resource |
| | | private TAccountingStrategyDetailService accountingStrategyDetailService; |
| | | @Resource |
| | | private TAccountingStrategyService accountingStrategyService; |
| | | |
| | | @Resource |
| | | private TPartnerSiteService partnerSiteService; |
| | |
| | | getSiteListDTO.setAuthDelete(roleType == 1 ? true : partnerService.authMenu(objectId, siteId, SiteMenu.SITE_DELETE)); |
| | | } |
| | | return pageInfo.setRecords(list); |
| | | } |
| | | |
| | | @Override |
| | | public List<Site> listSitesForExport(GetSiteList siteList) { |
| | | siteList.setPageCurr(1); |
| | | siteList.setPageSize(100000); |
| | | List<Integer> ids = getSiteList(siteList).getRecords().stream() |
| | | .map(GetSiteListDTO::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | return ids.isEmpty() ? Collections.emptyList() : listByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public SiteImportResultDTO importSites(MultipartFile file, Boolean cover) throws Exception { |
| | | if (file == null || file.isEmpty()) { |
| | | throw new ServiceException("请选择要导入的Excel文件"); |
| | | } |
| | | ExcelUtil<SiteExcelDTO> util = new ExcelUtil<>(SiteExcelDTO.class); |
| | | List<SiteExcelDTO> rows = util.importExcel(file.getInputStream(), 0); |
| | | if (rows == null || rows.isEmpty()) { |
| | | throw new ServiceException("导入文件没有数据"); |
| | | } |
| | | |
| | | SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); |
| | | Integer roleType = sysUser.getRoleType(); |
| | | Integer objectId = sysUser.getObjectId(); |
| | | Map<String, Partner> partnerMap = partnerService.list(new LambdaQueryWrapper<Partner>().eq(Partner::getDelFlag, 0)).stream() |
| | | .filter(item -> StringUtils.isNotEmpty(item.getName())) |
| | | .collect(Collectors.toMap(item -> normalizeImportValue(item.getName()), item -> item, (a, b) -> a)); |
| | | List<TAccountingStrategy> strategies = accountingStrategyService.list( |
| | | new LambdaQueryWrapper<TAccountingStrategy>().eq(TAccountingStrategy::getDelFlag, 0)); |
| | | Map<String, TAccountingStrategy> strategyMap = strategies.stream().filter(item -> StringUtils.isNotEmpty(item.getName())) |
| | | .collect(Collectors.toMap(item -> normalizeImportValue(item.getName()), item -> item, (a, b) -> a)); |
| | | List<Region> regions = otherClient.listAllRegion().getData(); |
| | | if (regions == null) { |
| | | regions = Collections.emptyList(); |
| | | } |
| | | Map<String, Region> regionByCode = regions.stream().filter(item -> StringUtils.isNotEmpty(item.getCode())) |
| | | .collect(Collectors.toMap(Region::getCode, item -> item, (a, b) -> a)); |
| | | Map<Integer, Region> regionById = regions.stream().filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(Region::getId, item -> item, (a, b) -> a)); |
| | | |
| | | SiteImportResultDTO result = new SiteImportResultDTO(); |
| | | List<Site> validRows = new ArrayList<>(); |
| | | Set<String> fileCodes = new HashSet<>(); |
| | | Set<String> duplicateCodes = new LinkedHashSet<>(); |
| | | for (int i = 0; i < rows.size(); i++) { |
| | | SiteExcelDTO row = rows.get(i); |
| | | int rowNum = i + 2; |
| | | List<String> errors = validateSiteImportRow(row, partnerMap, strategyMap, regionByCode, regionById, |
| | | roleType, objectId, fileCodes); |
| | | String code = row == null ? null : normalizeImportValue(row.getCode()); |
| | | if (!errors.isEmpty()) { |
| | | result.getErrorMessage().add(new SiteImportErrorDTO(rowNum, code, String.join(";", errors))); |
| | | continue; |
| | | } |
| | | Site site = buildImportedSite(row, partnerMap, strategyMap, regionByCode, regionById); |
| | | Site exists = getOne(new LambdaQueryWrapper<Site>().eq(Site::getCode, site.getCode()).eq(Site::getDelFlag, 0)); |
| | | if (exists != null) { |
| | | duplicateCodes.add(site.getCode()); |
| | | site.setId(exists.getId()); |
| | | site.setMark(exists.getMark()); |
| | | } |
| | | validRows.add(site); |
| | | } |
| | | result.setErrorCount(result.getErrorMessage().size()); |
| | | if (result.getErrorCount() > 0) { |
| | | return result; |
| | | } |
| | | if (!duplicateCodes.isEmpty() && !Boolean.TRUE.equals(cover)) { |
| | | result.setNeedConfirm(true); |
| | | result.setDuplicateCodes(new ArrayList<>(duplicateCodes)); |
| | | return result; |
| | | } |
| | | for (Site site : validRows) { |
| | | site.setDelFlag(false); |
| | | if (site.getId() == null) { |
| | | site.setMark(0); |
| | | save(site); |
| | | result.setSuccessCount(result.getSuccessCount() + 1); |
| | | } else { |
| | | updateById(site); |
| | | result.setUpdateCount(result.getUpdateCount() + 1); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private List<String> validateSiteImportRow(SiteExcelDTO row, Map<String, Partner> partnerMap, |
| | | Map<String, TAccountingStrategy> strategyMap, Map<String, Region> regionByCode, Map<Integer, Region> regionById, |
| | | Integer roleType, Integer objectId, Set<String> fileCodes) { |
| | | List<String> errors = new ArrayList<>(); |
| | | if (row == null) { |
| | | errors.add("空行"); |
| | | return errors; |
| | | } |
| | | String code = normalizeImportValue(row.getCode()); |
| | | String partnerName = normalizeImportValue(row.getPartnerName()); |
| | | Partner partner = partnerMap.get(partnerName); |
| | | if (StringUtils.isEmpty(partnerName)) errors.add("归属合作商/服务商不能为空"); |
| | | else if (partner == null || !Arrays.asList(1, 3).contains(partner.getPartnerType())) errors.add("归属合作商/服务商不存在"); |
| | | else if (Integer.valueOf(2).equals(roleType) && !Objects.equals(objectId, partner.getId())) errors.add("无权导入其他合作商/服务商站点"); |
| | | if (StringUtils.isNotEmpty(row.getVenuePartnerName())) { |
| | | Partner venue = partnerMap.get(normalizeImportValue(row.getVenuePartnerName())); |
| | | if (venue == null || !Integer.valueOf(2).equals(venue.getPartnerType())) errors.add("场地方不存在"); |
| | | } |
| | | if (StringUtils.isEmpty(code)) errors.add("站点编号不能为空"); |
| | | else if (!fileCodes.add(code)) errors.add("导入文件内站点编号重复"); |
| | | if (StringUtils.isEmpty(row.getName())) errors.add("站点名称不能为空"); |
| | | if (row.getSiteType() == null) errors.add("站点类型不能为空"); |
| | | if (row.getBusinessCategory() == null) errors.add("经营类别不能为空"); |
| | | if (row.getStatus() == null) errors.add("站点状态不能为空"); |
| | | if (row.getConstructionSite() == null) errors.add("建设场所不能为空"); |
| | | if (row.getTransformerPower() == null) errors.add("变压器功率不能为空"); |
| | | if (row.getMemberDiscountFlag() == null) errors.add("是否参与会员折扣不能为空"); |
| | | if (StringUtils.isEmpty(row.getAreaCode()) || !regionByCode.containsKey(row.getAreaCode().trim())) { |
| | | errors.add("所在区域行政区划代码不存在"); |
| | | } else { |
| | | Region district = regionByCode.get(row.getAreaCode().trim()); |
| | | Region city = regionById.get(district.getParentId()); |
| | | Region province = city == null ? null : regionById.get(city.getParentId()); |
| | | if (city == null || province == null) errors.add("所在区域必须填写区县级行政区划代码"); |
| | | } |
| | | if (StringUtils.isEmpty(row.getAddress())) errors.add("详细地址不能为空"); |
| | | if (StringUtils.isEmpty(row.getLon())) errors.add("经度不能为空"); |
| | | if (StringUtils.isEmpty(row.getLat())) errors.add("纬度不能为空"); |
| | | if (StringUtils.isEmpty(row.getCountryCode())) errors.add("充电站国家代码不能为空"); |
| | | if (StringUtils.isEmpty(row.getPhone())) errors.add("站点电话不能为空"); |
| | | if (StringUtils.isEmpty(row.getServicePhone())) errors.add("服务电话不能为空"); |
| | | if (row.getParkingSpace() == null) errors.add("车位数量不能为空"); |
| | | if (StringUtils.isEmpty(row.getRateDescription())) errors.add("充电电费描述不能为空"); |
| | | if (row.getSort() == null) errors.add("排序不能为空"); |
| | | if (row.getStationClassification() == null) errors.add("电站分类不能为空"); |
| | | if (row.getRoundTheClock() == null) errors.add("7*24小时营业不能为空"); |
| | | if (row.getParkType() == null) errors.add("停车费类型不能为空"); |
| | | if (row.getElectricityType() == null) errors.add("电费类型不能为空"); |
| | | if (row.getBusinessExpandType() == null) errors.add("报装类型不能为空"); |
| | | if (row.getCapacity() == null) errors.add("报装电源容量不能为空"); |
| | | if (row.getRatedPower() == null) errors.add("站点额定总功率不能为空"); |
| | | if (row.getPeriodFee() == null) errors.add("是否峰谷分时不能为空"); |
| | | if (StringUtils.isEmpty(row.getOfficialRunTime())) errors.add("正式投运时间不能为空"); |
| | | if (row.getVideoMonitor() == null) errors.add("视频监控配套情况不能为空"); |
| | | if (StringUtils.isEmpty(row.getEquipmentOwnerName())) errors.add("设备所属方名称不能为空"); |
| | | if (StringUtils.isEmpty(row.getResidentNo())) errors.add("供电局用户编号不能为空"); |
| | | if (row.getSupplyType() == null) errors.add("供电类型不能为空"); |
| | | if (StringUtils.isEmpty(row.getWattHourMeterNo())) errors.add("表号不能为空"); |
| | | if (row.getForwardPower() == null) errors.add("外电功率不能为空"); |
| | | if (StringUtils.isEmpty(row.getServiceFee())) errors.add("服务费描述不能为空"); |
| | | if (StringUtils.isEmpty(row.getRecordUniqueNo())) errors.add("充电站全省唯一备案号不能为空"); |
| | | if (StringUtils.isNotEmpty(row.getAccountingStrategyName()) && !strategyMap.containsKey(normalizeImportValue(row.getAccountingStrategyName()))) errors.add("计费模板不存在"); |
| | | if (StringUtils.isNotEmpty(row.getEstablishmentTime())) { |
| | | try { java.time.LocalDateTime.parse(row.getEstablishmentTime().trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } |
| | | catch (Exception e) { errors.add("建站时间格式应为yyyy-MM-dd HH:mm:ss"); } |
| | | } |
| | | return errors; |
| | | } |
| | | |
| | | private Site buildImportedSite(SiteExcelDTO row, Map<String, Partner> partnerMap, |
| | | Map<String, TAccountingStrategy> strategyMap, Map<String, Region> regionByCode, |
| | | Map<Integer, Region> regionById) { |
| | | Site site = new Site(); |
| | | BeanUtils.copyProperties(row, site); |
| | | site.setCode(normalizeImportValue(row.getCode())); |
| | | site.setName(normalizeImportValue(row.getName())); |
| | | site.setPartnerId(partnerMap.get(normalizeImportValue(row.getPartnerName())).getId()); |
| | | if (StringUtils.isNotEmpty(row.getVenuePartnerName())) { |
| | | site.setVenuePartnerId(partnerMap.get(normalizeImportValue(row.getVenuePartnerName())).getId()); |
| | | } |
| | | if (StringUtils.isNotEmpty(row.getAccountingStrategyName())) { |
| | | site.setAccountingStrategyId(strategyMap.get(normalizeImportValue(row.getAccountingStrategyName())).getId()); |
| | | } |
| | | Region district = regionByCode.get(row.getAreaCode().trim()); |
| | | Region city = regionById.get(district.getParentId()); |
| | | Region province = city == null ? null : regionById.get(city.getParentId()); |
| | | site.setDistricts(district.getName()); |
| | | site.setDistrictsCode(district.getCode()); |
| | | if (city != null) { |
| | | site.setCity(city.getName()); |
| | | site.setCityCode(city.getCode()); |
| | | } |
| | | if (province != null) { |
| | | site.setProvince(province.getName()); |
| | | site.setProvinceCode(province.getCode()); |
| | | } |
| | | if (StringUtils.isNotEmpty(row.getEstablishmentTime())) { |
| | | site.setEstablishmentTime(java.time.LocalDateTime.parse(row.getEstablishmentTime().trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); |
| | | } |
| | | site.setImgUrl(""); |
| | | return site; |
| | | } |
| | | |
| | | private String normalizeImportValue(String value) { |
| | | return value == null ? null : value.trim(); |
| | | } |
| | | |
| | | |
| | |
| | | if(null == site.getConstructionSite()){ |
| | | return AjaxResult.error("建设场所不能为空"); |
| | | } |
| | | if(null == site.getTransformerPower()){ |
| | | return AjaxResult.error("变压器功率不能为空"); |
| | | } |
| | | if(null == site.getMemberDiscountFlag()){ |
| | | return AjaxResult.error("请选择是否参与会员折扣"); |
| | | } |
| | | if(StringUtils.isEmpty(site.getProvince()) || StringUtils.isEmpty(site.getProvinceCode()) || |
| | | StringUtils.isEmpty(site.getCity()) || StringUtils.isEmpty(site.getCityCode()) || |
| | | StringUtils.isEmpty(site.getDistricts()) || StringUtils.isEmpty(site.getDistrictsCode())){ |
| | |
| | | TVip vip = JSON.parseObject(vipJson, TVip.class); |
| | | if(Objects.nonNull(vip) && vip.getType() == 2){ |
| | | list.forEach(item -> { |
| | | if(Objects.nonNull(vip.getDiscount())){ |
| | | if(Integer.valueOf(1).equals(item.getMemberDiscountFlag()) && Objects.nonNull(vip.getDiscount())){ |
| | | item.setVipElectrovalence(vip.getDiscount().divide(new BigDecimal(10)).multiply(item.getServiceCharge()).add(item.getElectrovalenceOriginal())); |
| | | } |
| | | }); |
| | |
| | | TVip vip = JSON.parseObject(vipJson, TVip.class); |
| | | if(Objects.nonNull(vip) && vip.getType() == 2){ |
| | | list.forEach(item -> { |
| | | item.setVipElectrovalence(vip.getDiscount().divide(new BigDecimal(10)).multiply(item.getServiceCharge()).add(item.getElectrovalenceOriginal())); |
| | | if (Integer.valueOf(1).equals(item.getMemberDiscountFlag())) { |
| | | item.setVipElectrovalence(vip.getDiscount().divide(new BigDecimal(10)).multiply(item.getServiceCharge()).add(item.getElectrovalenceOriginal())); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.chargingPile.api.model.Partner; |
| | | import com.ruoyi.chargingPile.api.model.TAccountingStrategy; |
| | | import com.ruoyi.chargingPile.api.query.TAccountingStrategyQuery; |
| | | import com.ruoyi.chargingPile.api.vo.TAccountingStrategyVO; |
| | | import com.ruoyi.chargingPile.api.vo.TParkingLotVO; |
| | | import com.ruoyi.chargingPile.mapper.TAccountingStrategyMapper; |
| | | import com.ruoyi.chargingPile.service.IPartnerService; |
| | | import com.ruoyi.chargingPile.service.TAccountingStrategyService; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import com.ruoyi.other.api.feignClient.OtherClient; |
| | |
| | | |
| | | @Resource |
| | | private SysUserClient sysUserClient; |
| | | @Resource |
| | | private IPartnerService partnerService; |
| | | @Override |
| | | public PageInfo<TAccountingStrategyVO> pageList(TAccountingStrategyQuery query) { |
| | | PageInfo<TAccountingStrategyVO> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize()); |
| | |
| | | tAccountingStrategyVO.setAuditStatus(auditRecord.getAuditStatus()); |
| | | tAccountingStrategyVO.setAuditRecordId(auditRecord.getId()); |
| | | tAccountingStrategyVO.setAuditParentId(auditRecord.getParentId()); |
| | | tAccountingStrategyVO.setPartnerId(auditRecord.getPartnerId()); |
| | | Partner partner = auditRecord.getPartnerId() == null |
| | | ? null : partnerService.getById(auditRecord.getPartnerId()); |
| | | tAccountingStrategyVO.setPartnerName(partner == null ? null : partner.getName()); |
| | | } |
| | | } |
| | | pageInfo.setRecords(list); |
| | |
| | | |
| | | <select id="pageList" resultType="com.ruoyi.chargingPile.api.vo.SiteVO"> |
| | | select |
| | | ts.id, ts.partner_id, ts.code, ts.`name`, ts.site_type, ts.business_category, ts.status, ts.construction_site, ts.img_url, |
| | | ts.id, ts.partner_id, ts.code, ts.`name`, ts.site_type, ts.business_category, ts.member_discount_flag as memberDiscountFlag, ts.status, ts.construction_site, ts.img_url, |
| | | ts.lon, ts.lat, ts.address, ts.country_code, ts.phone,ts.guide, ts.service_description, ts.vehicle_description, |
| | | ts.parking_space, ts.rate_description, ts.space_charge_explain, ts.accounting_strategy_id, ts.park_type as parkType, ts.park_fee as parkFee, ts.park_fee_free as parkFeeFree, ts.supportOrder as supportOrder, ts.equipment_owner_name as equipmentOwnerName, ts.rated_power as ratedPower, ts.del_flag,tcg.fastCount, |
| | | tcg.slowCount,tcg.superCount,tasd.electrovalence,tasd.vipElectrovalence,tasd.serviceCharge,tasd.electrovalenceOriginal,tcg.superFreeCount,tcg.fastFreeCount,tcg.slowFreeCount, |
| | | tcg.slowCount,tcg.superCount,tasd.electrovalence, |
| | | CASE WHEN ts.member_discount_flag = 1 THEN tasd.vipElectrovalence ELSE tasd.electrovalence END as vipElectrovalence, |
| | | tasd.serviceCharge,tasd.electrovalenceOriginal,tcg.superFreeCount,tcg.fastFreeCount,tcg.slowFreeCount, |
| | | ROUND( |
| | | 6378.138 * 2 * ASIN( |
| | | SQRT( |
| | |
| | | ) * 1000 |
| | | ) AS distance |
| | | from t_site ts |
| | | left join t_partner tp on (ts.partner_id = tp.id) |
| | | left join ( |
| | | select site_id, |
| | | SUM(CASE WHEN charge_mode = 1 THEN 1 ELSE 0 END) AS superCount, |
| | |
| | | AND ts.del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()} |
| | | AND ts.status = ${@com.ruoyi.common.core.enums.status.SiteStatusEnum@NORMAL_USE.getCode()} |
| | | AND ts.accounting_strategy_id IS NOT NULL |
| | | AND (tp.partner_type IS NULL OR tp.partner_type != 3) |
| | | </where> |
| | | <if test="query.sortType != null"> |
| | | <choose> |
| | |
| | | </if> |
| | | </select> |
| | | <select id="getDetailById" resultType="com.ruoyi.chargingPile.api.vo.SiteDetailVO"> |
| | | SELECT id, partner_id, venue_partner_id, code, `name`, site_type, business_category, status, construction_site, img_url, province, province_code, |
| | | SELECT id, partner_id, venue_partner_id, code, `name`, site_type, business_category, member_discount_flag as memberDiscountFlag, status, construction_site, img_url, province, province_code, |
| | | city, city_code, districts, districts_code, lon, lat, address, country_code, phone, service_phone, guide, start_service_time, |
| | | end_service_time, service_description, vehicle_description, parking_space, rate_description, space_charge_explain, |
| | | accounting_strategy_id, establishment_time, sort, remark, create_time, del_flag, mark, supportOrder, |
| | |
| | | ) * 1000 |
| | | ) AS distance |
| | | FROM t_site WHERE id = #{query.siteId} AND del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()} |
| | | AND NOT EXISTS (SELECT 1 FROM t_partner p WHERE p.id = t_site.partner_id AND p.partner_type = 3) |
| | | </select> |
| | | <select id="getSiteListParkLot" resultType="com.ruoyi.chargingPile.api.model.Site"> |
| | | select ts.id, ts.partner_id, ts.venue_partner_id, ts.code, ts.`name`, ts.site_type, ts.business_category, ts.status |
| | |
| | | </select> |
| | | <select id="getMapSiteList" resultType="com.ruoyi.chargingPile.api.vo.SiteVO"> |
| | | select |
| | | ts.id, ts.partner_id, ts.code, ts.`name`, ts.site_type, ts.business_category, ts.status, ts.construction_site, ts.img_url, |
| | | ts.id, ts.partner_id, ts.code, ts.`name`, ts.site_type, ts.business_category, ts.member_discount_flag as memberDiscountFlag, ts.status, ts.construction_site, ts.img_url, |
| | | ts.lon, ts.lat, ts.address, ts.country_code, ts.phone,ts.guide, ts.service_description, ts.vehicle_description, |
| | | ts.parking_space, ts.rate_description, ts.space_charge_explain, ts.accounting_strategy_id, ts.park_type as parkType, ts.park_fee as parkFee, ts.park_fee_free as parkFeeFree, ts.supportOrder as supportOrder, ts.equipment_owner_name as equipmentOwnerName, ts.rated_power as ratedPower, ts.del_flag,tcg.fastCount, |
| | | tcg.slowCount,tcg.superCount,tasd.electrovalence,tasd.vipElectrovalence,tcg.superFreeCount,tcg.fastFreeCount,tcg.slowFreeCount, |
| | | tcg.slowCount,tcg.superCount,tasd.electrovalence, |
| | | CASE WHEN ts.member_discount_flag = 1 THEN tasd.vipElectrovalence ELSE tasd.electrovalence END as vipElectrovalence, |
| | | tcg.superFreeCount,tcg.fastFreeCount,tcg.slowFreeCount, |
| | | ROUND( |
| | | 6378.138 * 2 * ASIN( |
| | | SQRT( |
| | |
| | | ) * 1000 |
| | | ) AS distance |
| | | from t_site ts |
| | | left join t_partner tp on (ts.partner_id = tp.id) |
| | | left join ( |
| | | select site_id, |
| | | SUM(CASE WHEN charge_mode = 1 THEN 1 ELSE 0 END) AS superCount, |
| | |
| | | </if> |
| | | AND ts.del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()} |
| | | AND ts.status = ${@com.ruoyi.common.core.enums.status.SiteStatusEnum@NORMAL_USE.getCode()} |
| | | AND (tp.partner_type IS NULL OR tp.partner_type != 3) |
| | | </where> |
| | | <if test="query.sortType != null"> |
| | | <choose> |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.Wrapper; |
| | | import com.ruoyi.chargingPile.api.model.Site; |
| | | import com.ruoyi.chargingPile.dto.BatchSetSiteAccountingStrategyDTO; |
| | | import com.ruoyi.chargingPile.service.ISiteService; |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | |
| | | assertTrue(result.getData().isEmpty()); |
| | | verify(siteService, never()).list(any(Wrapper.class)); |
| | | } |
| | | |
| | | @Test |
| | | void batchSetAccountingStrategyUsesJsonRequestBody() throws NoSuchMethodException { |
| | | SiteController controller = new SiteController(); |
| | | ISiteService siteService = mock(ISiteService.class); |
| | | ReflectionTestUtils.setField(controller, "siteService", siteService); |
| | | BatchSetSiteAccountingStrategyDTO request = new BatchSetSiteAccountingStrategyDTO(); |
| | | request.setIds(Arrays.asList(11, 12)); |
| | | request.setAccountingStrategyId(23); |
| | | |
| | | controller.batchSetAccountingStrategy(request); |
| | | |
| | | assertNotNull(SiteController.class |
| | | .getMethod("batchSetAccountingStrategy", BatchSetSiteAccountingStrategyDTO.class) |
| | | .getParameters()[0] |
| | | .getAnnotation(RequestBody.class)); |
| | | verify(siteService).batchSetAccountingStrategy(Arrays.asList(11, 12), 23); |
| | | } |
| | | |
| | | @Test |
| | | void batchSetAccountingStrategyRejectsEmptySiteSelection() { |
| | | SiteController controller = new SiteController(); |
| | | ISiteService siteService = mock(ISiteService.class); |
| | | ReflectionTestUtils.setField(controller, "siteService", siteService); |
| | | BatchSetSiteAccountingStrategyDTO request = new BatchSetSiteAccountingStrategyDTO(); |
| | | request.setIds(Collections.emptyList()); |
| | | request.setAccountingStrategyId(23); |
| | | |
| | | AjaxResult result = controller.batchSetAccountingStrategy(request); |
| | | |
| | | assertTrue(result.isError()); |
| | | verify(siteService, never()).batchSetAccountingStrategy(any(), any()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.dto; |
| | | |
| | | import com.ruoyi.chargingPile.api.model.Site; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | |
| | | class SiteExcelDTOTest { |
| | | |
| | | @Test |
| | | void columnsFollowSitePrototypeOrder() { |
| | | List<String> actual = Arrays.stream(SiteExcelDTO.class.getDeclaredFields()) |
| | | .map(field -> field.getAnnotation(Excel.class)) |
| | | .filter(annotation -> annotation != null) |
| | | .map(Excel::name) |
| | | .collect(Collectors.toList()); |
| | | assertEquals(Arrays.asList( |
| | | "*归属合作商/服务商", "场地方", "*站点编号", "*站点名称", "*站点类型", "*经营类别", |
| | | "*站点状态", "*建设场所", "*变压器功率", "*是否参与会员折扣", "*所在区域", "*详细地址", |
| | | "*经度", "*纬度", "*充电站国家代码", "*站点电话", "*服务电话", "站点引导", "运营时间", |
| | | "站内提供服务", "车型描述", "*车位数量", "*充电电费描述", "超时占位费描述", "计费模板", |
| | | "建站时间", "*排序", "备注", "*电站分类", "通用类型", "充换电站所在县以下行政区划代码", |
| | | "*7*24小时营业", "*停车费类型", "停车费描述", "服务车型描述", "支付方式", "是否支持预约", |
| | | "*电费类型", "*报装类型", "*报装电源容量", "*站点额定总功率", "*是否峰谷分时", "*正式投运时间", |
| | | "充换电站方位", "充换电站建设面积", "充换电站人工值守", "*视频监控配套情况", "周边配套设施", |
| | | "是否有小票机", "是否有道闸", "是否有地锁", "*设备所属方名称", "*供电局用户编号", "*供电类型", |
| | | "*表号", "*外电功率", "*服务费描述", "*充电站全省唯一备案号" |
| | | ), actual); |
| | | } |
| | | |
| | | @Test |
| | | void exportRowContainsNewSiteFieldsAndNames() { |
| | | Site site = new Site(); |
| | | site.setCode("SITE001"); |
| | | site.setTransformerPower(new BigDecimal("630.00")); |
| | | site.setMemberDiscountFlag(1); |
| | | site.setDistrictsCode("510903"); |
| | | |
| | | SiteExcelDTO row = SiteExcelDTO.from(site, "合作商A", "场地方B", "计费模板C"); |
| | | |
| | | assertEquals("SITE001", row.getCode()); |
| | | assertEquals(new BigDecimal("630.00"), row.getTransformerPower()); |
| | | assertEquals(1, row.getMemberDiscountFlag()); |
| | | assertEquals("510903", row.getAreaCode()); |
| | | assertEquals("合作商A", row.getPartnerName()); |
| | | assertEquals("场地方B", row.getVenuePartnerName()); |
| | | assertEquals("计费模板C", row.getAccountingStrategyName()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.export; |
| | | |
| | | import com.ruoyi.chargingPile.api.dto.PageChargingPileListDTO; |
| | | import com.ruoyi.chargingPile.api.model.TChargingPile; |
| | | import com.ruoyi.chargingPile.api.vo.TChargingGunVO; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.lang.reflect.Field; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | |
| | | class ChargingPileExportTest { |
| | | |
| | | @Test |
| | | void shouldMatchPrototypeColumnOrder() { |
| | | List<String> actual = Arrays.stream(ChargingPileExport.class.getDeclaredFields()) |
| | | .map(field -> field.getAnnotation(Excel.class)) |
| | | .filter(annotation -> annotation != null) |
| | | .map(Excel::name) |
| | | .collect(Collectors.toList()); |
| | | |
| | | assertEquals(Arrays.asList( |
| | | "*设备编码", "*设备名称", "*桩号", "设备类型", "归属电站", "设备合作商", |
| | | "生产机构代码", "生产商名称", "设备型号", "设备生产日期", "*额定功率", |
| | | "设备充电站编号", "设备唯一编码", "*设备分类", "设备经度", "设备纬度", |
| | | "*接口编码", "*接口名称", "*设备接口分类", "*接口类型", "*接口状态", "*充电方式", |
| | | "计费策略", "*额定电压上限", "*额定电压下限", "恒功率电压上限", "恒功率电压下限", |
| | | "*额定电流", "恒功率电流上限", "恒功率电流下限", "*额定功率", "车位号", |
| | | "车位状态", "地锁状态", "*国家标准", "接口说明", "辅助电源", "*运营状态", "运营时间" |
| | | ), actual); |
| | | } |
| | | |
| | | @Test |
| | | void shouldCombinePileAndGunIntoOneExportRow() { |
| | | PageChargingPileListDTO record = new PageChargingPileListDTO(); |
| | | record.setSiteName("测试电站"); |
| | | record.setPartnerName("测试合作商"); |
| | | TChargingPile pile = new TChargingPile(); |
| | | pile.setCode("PILE001"); |
| | | pile.setName("1号桩"); |
| | | pile.setNumber(1); |
| | | pile.setManufacturerCode("ORG001"); |
| | | pile.setProductionDate(LocalDate.of(2026, 7, 16)); |
| | | TChargingGunVO gun = new TChargingGunVO(); |
| | | gun.setCode("01"); |
| | | gun.setName("1号枪"); |
| | | gun.setStrategyName("标准计费"); |
| | | gun.setRatedPower(new BigDecimal("120")); |
| | | gun.setOpreateStatus(50); |
| | | |
| | | ChargingPileExport actual = ChargingPileExport.from(record, pile, gun); |
| | | |
| | | assertEquals("PILE001", actual.getCode()); |
| | | assertEquals("测试电站", actual.getSiteName()); |
| | | assertEquals("测试合作商", actual.getPartnerName()); |
| | | assertEquals("ORG001", actual.getManufacturerCode()); |
| | | assertEquals("2026-07-16", actual.getProductionDate()); |
| | | assertEquals("01", actual.getInterfaceCode()); |
| | | assertEquals("1号枪", actual.getInterfaceName()); |
| | | assertEquals("标准计费", actual.getStrategyName()); |
| | | assertEquals(new BigDecimal("120"), actual.getInterfaceRatedPower()); |
| | | assertEquals(50, actual.getOperateStatus()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.mapper; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | |
| | | class SiteMapperVisibilityTest { |
| | | |
| | | @Test |
| | | void miniProgramQueriesExcludeServiceProviderSitesAndExposeMemberDiscountFlag() throws Exception { |
| | | String mapper = new String(Files.readAllBytes(Paths.get(getClass().getClassLoader() |
| | | .getResource("mapper/chargingPile/SiteMapper.xml").toURI())), StandardCharsets.UTF_8); |
| | | |
| | | assertTrue(mapper.contains("tp.partner_type != 3")); |
| | | assertTrue(mapper.contains("p.partner_type = 3")); |
| | | assertTrue(mapper.contains("member_discount_flag as memberDiscountFlag")); |
| | | assertTrue(mapper.contains("CASE WHEN ts.member_discount_flag = 1 THEN tasd.vipElectrovalence")); |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.chargingPile.service.impl; |
| | | |
| | | import com.ruoyi.chargingPile.api.model.Partner; |
| | | import com.ruoyi.chargingPile.mapper.PartnerMapper; |
| | | import com.baomidou.mybatisplus.core.conditions.Wrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | import org.mockito.ArgumentCaptor; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | import static org.mockito.ArgumentMatchers.isNull; |
| | | |
| | | class PartnerServiceImplTest { |
| | | |
| | |
| | | |
| | | assertTrue(new PartnerServiceImpl().addPartnerParameterCheck(partner).isSuccess()); |
| | | } |
| | | |
| | | @Test |
| | | void venuePartnerFallsBackToPartnerRoleWhenDedicatedRoleIsMissing() { |
| | | PartnerServiceImpl service = new PartnerServiceImpl(); |
| | | PartnerMapper mapper = mock(PartnerMapper.class); |
| | | ReflectionTestUtils.setField(service, "baseMapper", mapper); |
| | | when(mapper.getDefaultPartnerRoleId("场地方")).thenReturn(null); |
| | | when(mapper.getDefaultPartnerRoleId("合作商")).thenReturn(7); |
| | | |
| | | assertEquals(7, service.resolveDefaultRoleId(2)); |
| | | verify(mapper).getDefaultPartnerRoleId("场地方"); |
| | | verify(mapper).getDefaultPartnerRoleId("合作商"); |
| | | } |
| | | |
| | | @Test |
| | | void serviceProviderUsesDedicatedRoleWhenItExists() { |
| | | PartnerServiceImpl service = new PartnerServiceImpl(); |
| | | PartnerMapper mapper = mock(PartnerMapper.class); |
| | | ReflectionTestUtils.setField(service, "baseMapper", mapper); |
| | | when(mapper.getDefaultPartnerRoleId("服务商")).thenReturn(9); |
| | | |
| | | assertEquals(9, service.resolveDefaultRoleId(3)); |
| | | verify(mapper).getDefaultPartnerRoleId("服务商"); |
| | | verify(mapper, never()).getDefaultPartnerRoleId("合作商"); |
| | | } |
| | | |
| | | @Test |
| | | void clearingShareRatiosStillProducesExplicitDatabaseUpdate() { |
| | | PartnerServiceImpl service = new PartnerServiceImpl(); |
| | | PartnerMapper mapper = mock(PartnerMapper.class); |
| | | ReflectionTestUtils.setField(service, "baseMapper", mapper); |
| | | Partner partner = new Partner(); |
| | | partner.setId(8); |
| | | partner.setElectricityShareRatio(null); |
| | | partner.setServiceShareRatio(null); |
| | | |
| | | service.updateShareRatios(partner); |
| | | |
| | | ArgumentCaptor<Wrapper<Partner>> wrapperCaptor = ArgumentCaptor.forClass(Wrapper.class); |
| | | verify(mapper).update(isNull(), wrapperCaptor.capture()); |
| | | UpdateWrapper<Partner> wrapper = (UpdateWrapper<Partner>) wrapperCaptor.getValue(); |
| | | assertTrue(wrapper.getSqlSet().contains("electricity_share_ratio")); |
| | | assertTrue(wrapper.getSqlSet().contains("service_share_ratio")); |
| | | } |
| | | } |
| | |
| | | @PostMapping(value = "/chargingOrder") |
| | | @ApiOperation(value = "充电桩订单列表", tags = {"管理后台-订单管理"}) |
| | | public AjaxResult<TCharingOrderVO> chargingOrder(@RequestBody ChargingOrderQuery dto) { |
| | | List<Long> data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); |
| | | dto.setUserIds(data); |
| | | TCharingOrderVO res = chargingOrderService.chargingOrder(dto); |
| | | return AjaxResult.success(res); |
| | | } |
| | |
| | | return chargingOrderService.paymentChargingOrder(addChargingOrder); |
| | | } |
| | | |
| | | @ResponseBody |
| | | @GetMapping(value = "/confirmIcbcPayment") |
| | | @ApiOperation(value = "主动确认工行充电支付结果", tags = {"小程序-扫一扫"}) |
| | | public AjaxResult confirmIcbcPayment(@RequestParam("orderCode") String orderCode) { |
| | | return chargingOrderService.confirmIcbcPayment(orderCode); |
| | | } |
| | | |
| | | @RequiresPermissions(value = {"/chargingPileOrder", "/paymentOrder/refund"}, logical = Logical.AND) |
| | | @PostMapping("/icbc/fund/payment-query") |
| | | @ApiOperation(value = "管理后台查询工行支付订单", tags = {"后台-充电桩订单"}) |
| | |
| | | */ |
| | | AjaxResult paymentChargingOrder(AddChargingOrder addChargingOrder); |
| | | |
| | | AjaxResult confirmIcbcPayment(String orderCode); |
| | | |
| | | ChargingSimulationVO getCurrentSimulationOrder(Integer gunId); |
| | | |
| | | ChargingSimulationVO completeSimulation(ChargingSimulationReq request); |
| New file |
| | |
| | | package com.ruoyi.order.service.impl; |
| | | |
| | | import com.ruoyi.other.api.domain.TCoupon; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | |
| | | final class ChargingCouponSupport { |
| | | private static final BigDecimal TEN = new BigDecimal("10"); |
| | | |
| | | private ChargingCouponSupport() { |
| | | } |
| | | |
| | | static BigDecimal calculateUpfrontDiscount(TCoupon coupon, BigDecimal rechargeAmount) { |
| | | if (coupon == null || rechargeAmount == null || rechargeAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | if (!Integer.valueOf(1).equals(coupon.getType())) { |
| | | throw new IllegalArgumentException("请选择充电优惠券"); |
| | | } |
| | | BigDecimal threshold = coupon.getMeetTheConditions() == null |
| | | ? BigDecimal.ZERO : coupon.getMeetTheConditions(); |
| | | if (rechargeAmount.compareTo(threshold) < 0) { |
| | | throw new IllegalArgumentException("预付金额未达到优惠券使用门槛"); |
| | | } |
| | | |
| | | BigDecimal discount; |
| | | if (Integer.valueOf(1).equals(coupon.getPreferentialMode())) { |
| | | discount = coupon.getDiscountAmount(); |
| | | } else if (Integer.valueOf(2).equals(coupon.getPreferentialMode())) { |
| | | if (coupon.getDiscount() == null) { |
| | | throw new IllegalArgumentException("优惠券折扣配置无效"); |
| | | } |
| | | discount = rechargeAmount.multiply(TEN.subtract(coupon.getDiscount())) |
| | | .divide(TEN, 2, RoundingMode.HALF_UP); |
| | | if (coupon.getMaximumDiscountAmount() != null) { |
| | | discount = discount.min(coupon.getMaximumDiscountAmount()); |
| | | } |
| | | } else { |
| | | throw new IllegalArgumentException("优惠券优惠方式无效"); |
| | | } |
| | | if (discount == null || discount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new IllegalArgumentException("优惠券优惠金额无效"); |
| | | } |
| | | return discount.min(rechargeAmount).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.order.service.impl; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | final class ChargingPaymentSupport { |
| | | private ChargingPaymentSupport() { |
| | | } |
| | | |
| | | static int resolvePayMethod(BigDecimal balancePayAmount, BigDecimal thirdPayAmount, int fallback) { |
| | | boolean hasBalance = balancePayAmount != null && balancePayAmount.compareTo(BigDecimal.ZERO) > 0; |
| | | boolean hasThirdPay = thirdPayAmount != null && thirdPayAmount.compareTo(BigDecimal.ZERO) > 0; |
| | | if (hasBalance && hasThirdPay) { |
| | | return 2; |
| | | } |
| | | if (hasBalance) { |
| | | return 1; |
| | | } |
| | | if (hasThirdPay) { |
| | | return 3; |
| | | } |
| | | return fallback; |
| | | } |
| | | } |
| | |
| | | return chargingOrder != null && "ICBC".equals(chargingOrder.getSourceName()); |
| | | } |
| | | |
| | | private BigDecimal resolveUpfrontCouponDiscount(Long appCouponId, Long userId, BigDecimal rechargeAmount) { |
| | | TAppCoupon appCoupon = appCouponClient.getAppCouponById(appCouponId).getData(); |
| | | if (appCoupon == null || !userId.equals(appCoupon.getAppUserId())) { |
| | | throw new ServiceException("优惠券不存在或不属于当前用户"); |
| | | } |
| | | if (!Integer.valueOf(1).equals(appCoupon.getStatus())) { |
| | | throw new ServiceException("优惠券已使用"); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if ((appCoupon.getStartTime() != null && appCoupon.getStartTime().isAfter(now)) |
| | | || (appCoupon.getEndTime() != null && appCoupon.getEndTime().isBefore(now))) { |
| | | throw new ServiceException("优惠券不在有效期内"); |
| | | } |
| | | try { |
| | | TCoupon coupon = JSON.parseObject(appCoupon.getCouponJson(), TCoupon.class); |
| | | return ChargingCouponSupport.calculateUpfrontDiscount(coupon, rechargeAmount); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 判断订单是否为白名单订单 |
| | | * |
| | |
| | | AppUserWhitelistMatchVO whitelist = appUserWhitelistClient.matchWhitelist(userId, tChargingGun.getSiteId()).getData(); |
| | | boolean whitelistMatched = whitelist != null && Boolean.TRUE.equals(whitelist.getMatched()); |
| | | if (whitelistMatched) { |
| | | payMethod = 3; |
| | | addChargingOrder.setPayMethod(3); |
| | | addChargingOrder.setAppUserCouponId(null); |
| | | } |
| | | boolean whitelistAllFree = whitelistMatched |
| | | && Integer.valueOf(WHITELIST_BENEFIT_ALL_FREE).equals(whitelist.getBenefitType()); |
| | | BigDecimal userBalance = money(appUser.getBalance()); |
| | | BigDecimal balancePayAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal thirdPayAmount = rechargeAmount; |
| | | BigDecimal upfrontCouponDiscount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | if (payMethod == 2 && userBalance.compareTo(rechargeAmount) >= 0) { |
| | | payMethod = 1; |
| | | addChargingOrder.setPayMethod(1); |
| | | } |
| | | if (isBalancePayMode(payMethod)) { |
| | | addChargingOrder.setAppUserCouponId(null); |
| | | if (payMethod == 1 && userBalance.compareTo(rechargeAmount) < 0) { |
| | | } else if (!whitelistMatched && addChargingOrder.getAppUserCouponId() != null) { |
| | | upfrontCouponDiscount = resolveUpfrontCouponDiscount( |
| | | addChargingOrder.getAppUserCouponId(), userId, rechargeAmount); |
| | | } |
| | | BigDecimal payableAmount = whitelistAllFree |
| | | ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) |
| | | : rechargeAmount.subtract(upfrontCouponDiscount).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal thirdPayAmount = payableAmount; |
| | | if (isBalancePayMode(payMethod)) { |
| | | if (payMethod == 1 && userBalance.compareTo(payableAmount) < 0) { |
| | | return AjaxResult.error("余额不足,请选择组合支付或第三方支付"); |
| | | } |
| | | balancePayAmount = payMethod == 1 ? rechargeAmount : userBalance.min(rechargeAmount).setScale(2, RoundingMode.HALF_UP); |
| | | thirdPayAmount = rechargeAmount.subtract(balancePayAmount).setScale(2, RoundingMode.HALF_UP); |
| | | balancePayAmount = payMethod == 1 ? payableAmount : userBalance.min(payableAmount).setScale(2, RoundingMode.HALF_UP); |
| | | thirdPayAmount = payableAmount.subtract(balancePayAmount).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | if (whitelistMatched) { |
| | | balancePayAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | thirdPayAmount = Integer.valueOf(WHITELIST_BENEFIT_ALL_FREE).equals(whitelist.getBenefitType()) |
| | | ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) |
| | | : rechargeAmount; |
| | | } |
| | | //查询待支付状态的订单,没有支付则删除订单 |
| | | List<TChargingOrder> list = this.list(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, userId) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | for (TChargingOrder tChargingOrder : list) { |
| | | Integer rechargePaymentType = tChargingOrder.getRechargePaymentType(); |
| | | if ("ICBC".equals(tChargingOrder.getSourceName())) { |
| | | IcbcPaymentResp data = icbcPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data && isIcbcRemovableStatus(data.getStatus(), tChargingOrder)) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 1 == rechargePaymentType) { |
| | | NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTrade_state(); |
| | | //支付失败,删除无效的订单 |
| | | if (trade_state.equals("REFUND") || trade_state.equals("NOTPAY") || trade_state.equals("REVOKED") || trade_state.equals("PAYERROR")) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | } |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 2 == rechargePaymentType) { |
| | | AliQueryOrder data = aliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | | String tradeStatus = data.getTradeStatus(); |
| | | if (null != tradeStatus && tradeStatus.equals("TRADE_CLOSED")) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | } |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 3 == rechargePaymentType) { |
| | | AliQueryOrder data = h5AliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | | String tradeStatus = data.getTradeStatus(); |
| | | if (null != tradeStatus && tradeStatus.equals("TRADE_CLOSED")) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | payMethod = ChargingPaymentSupport.resolvePayMethod(balancePayAmount, thirdPayAmount, payMethod); |
| | | addChargingOrder.setPayMethod(payMethod); |
| | | //检查当前枪是否是正在使用中 |
| | | TChargingOrder one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) |
| | | .in(TChargingOrder::getStatus, Arrays.asList(3)).eq(TChargingOrder::getDelFlag, 0)); |
| | |
| | | } |
| | | //查询当前充电枪是否有正在支付的订单 |
| | | one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) |
| | | .eq(TChargingOrder::getAppUserCarId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | .eq(TChargingOrder::getAppUserId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1) |
| | | .eq(TChargingOrder::getDelFlag, 0).last("limit 1")); |
| | | if (null != one) { |
| | | //查询三方支付数据,支付中直接结束三方订单 |
| | | if ("ICBC".equals(one.getSourceName())) { |
| | |
| | | chargingOrder.setThirdRefundAmount(BigDecimal.ZERO); |
| | | chargingOrder.setRechargeAmount(rechargeAmount); |
| | | chargingOrder.setAppCouponId(addChargingOrder.getAppUserCouponId()); |
| | | chargingOrder.setCouponDiscountAmount(upfrontCouponDiscount); |
| | | chargingOrder.setIsWhitelist(whitelistMatched ? 1 : 0); |
| | | chargingOrder.setWhitelistId(whitelistMatched ? whitelist.getWhitelistId() : null); |
| | | chargingOrder.setWhitelistBenefitType(whitelistMatched ? whitelist.getBenefitType() : null); |
| | |
| | | TAccountingStrategy accountingStrategy = accountingStrategyClient.getAccountingStrategyById(accountingStrategyId).getData(); |
| | | |
| | | //会员折扣支持直营网点和加盟商站点 |
| | | if (!whitelistMatched && !isBalancePayMode(payMethod) && null != appUser.getVipId()) { |
| | | if (!whitelistMatched && Integer.valueOf(1).equals(site.getMemberDiscountFlag()) |
| | | && !isBalancePayMode(payMethod) && null != appUser.getVipId()) { |
| | | GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); |
| | | getAppUserVipDetail.setAppUserId(appUser.getId()); |
| | | getAppUserVipDetail.setVipId(appUser.getVipId()); |
| | |
| | | public IcbcPaymentResp queryIcbcAdminPayment(String orderCode) { |
| | | TChargingOrder order = loadIcbcAdminOrder(orderCode, false); |
| | | return requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败"); |
| | | } |
| | | |
| | | @Transactional |
| | | @Override |
| | | public AjaxResult confirmIcbcPayment(String orderCode) { |
| | | if (!StringUtils.hasLength(orderCode)) { |
| | | return AjaxResult.error("订单号不能为空"); |
| | | } |
| | | Long userId = tokenService.getLoginUserApplet().getUserId(); |
| | | TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>() |
| | | .eq(TChargingOrder::getCode, orderCode) |
| | | .eq(TChargingOrder::getAppUserId, userId) |
| | | .eq(TChargingOrder::getDelFlag, 0) |
| | | .last("limit 1")); |
| | | if (order == null) { |
| | | return AjaxResult.error("支付订单不存在"); |
| | | } |
| | | if (Integer.valueOf(2).equals(order.getRechargePaymentStatus())) { |
| | | return AjaxResult.success(Collections.singletonMap("confirmed", true)); |
| | | } |
| | | if (!isIcbcOrder(order)) { |
| | | return AjaxResult.error("当前订单不是工行支付订单"); |
| | | } |
| | | |
| | | R<IcbcPaymentResp> queryResult = icbcPaymentClient.query(orderCode); |
| | | IcbcPaymentResp payment = queryResult == null ? null : queryResult.getData(); |
| | | if (!IcbcAdminFundOperationSupport.isBankPaid(payment) |
| | | || payment == null |
| | | || !orderCode.equals(payment.getOrderId()) |
| | | || !StringUtils.hasLength(payment.getTradeNo())) { |
| | | return AjaxResult.success(Collections.singletonMap("confirmed", false)); |
| | | } |
| | | AjaxResult callbackResult = chargingOrderCallback(1, orderCode, payment.getTradeNo(), "1"); |
| | | if (!Integer.valueOf(200).equals(callbackResult.get("code"))) { |
| | | return callbackResult; |
| | | } |
| | | return AjaxResult.success(Collections.singletonMap("confirmed", true)); |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | //计算优惠券 |
| | | BigDecimal couponDiscount = BigDecimal.ZERO; |
| | | boolean couponAppliedUpfront = money(chargingOrder.getCouponDiscountAmount()).compareTo(BigDecimal.ZERO) > 0; |
| | | BigDecimal couponDiscount = couponAppliedUpfront |
| | | ? money(chargingOrder.getCouponDiscountAmount()) : BigDecimal.ZERO; |
| | | if (!isWhitelistOrder(chargingOrder) && !isBalancePayMode(chargingOrder.getPayMethod()) && null != chargingOrder.getAppCouponId()) { |
| | | //判断实际充电金额是否满足优惠券使用条件,如果不满足则不适用优惠券。 |
| | | TAppCoupon appCoupon = appCouponClient.getAppCouponById(chargingOrder.getAppCouponId()).getData(); |
| | | String couponJson = appCoupon.getCouponJson(); |
| | | TCoupon tCoupon = JSON.parseObject(couponJson, TCoupon.class); |
| | | Integer preferentialMode = tCoupon.getPreferentialMode(); |
| | | if (1 == preferentialMode) { |
| | | if (couponAppliedUpfront) { |
| | | appCoupon.setStatus(2); |
| | | appCouponClient.updateAppCoupon(appCoupon); |
| | | } else if (1 == preferentialMode) { |
| | | //满减 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | BigDecimal couponDiscountAmount = tCoupon.getDiscountAmount(); |
| | |
| | | chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); |
| | | } |
| | | } |
| | | if (2 == preferentialMode) { |
| | | if (!couponAppliedUpfront && 2 == preferentialMode) { |
| | | //抵扣 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | //折扣金额 |
| | |
| | | } |
| | | //优惠券优惠金额 |
| | | couponDiscount = couponDiscount.setScale(4, RoundingMode.HALF_EVEN); |
| | | //退款金额+优惠券 |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount); |
| | | if (serviceCharge_total.compareTo(BigDecimal.ZERO) > 0) { |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount); |
| | | //历史订单在结算时计算优惠;新订单已在预付时少收优惠金额,不能再次增加退款。 |
| | | if (!couponAppliedUpfront) { |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | } |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setWhitelistDiscountAmount(whitelistDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setWhitelistElectricRefundAmount(electricDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | |
| | | } |
| | | |
| | | //计算优惠券 |
| | | BigDecimal couponDiscount = BigDecimal.ZERO; |
| | | boolean couponAppliedUpfront = money(chargingOrder.getCouponDiscountAmount()).compareTo(BigDecimal.ZERO) > 0; |
| | | BigDecimal couponDiscount = couponAppliedUpfront |
| | | ? money(chargingOrder.getCouponDiscountAmount()) : BigDecimal.ZERO; |
| | | if (null != chargingOrder.getAppCouponId()) { |
| | | //判断实际充电金额是否满足优惠券使用条件,如果不满足则不适用优惠券。 |
| | | TAppCoupon appCoupon = appCouponClient.getAppCouponById(chargingOrder.getAppCouponId()).getData(); |
| | | String couponJson = appCoupon.getCouponJson(); |
| | | TCoupon tCoupon = JSON.parseObject(couponJson, TCoupon.class); |
| | | Integer preferentialMode = tCoupon.getPreferentialMode(); |
| | | if (1 == preferentialMode) { |
| | | if (couponAppliedUpfront) { |
| | | appCoupon.setStatus(2); |
| | | appCouponClient.updateAppCoupon(appCoupon); |
| | | } else if (1 == preferentialMode) { |
| | | //满减 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | BigDecimal couponDiscountAmount = tCoupon.getDiscountAmount(); |
| | |
| | | chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); |
| | | } |
| | | } |
| | | if (2 == preferentialMode) { |
| | | if (!couponAppliedUpfront && 2 == preferentialMode) { |
| | | //抵扣 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | //折扣金额 |
| | |
| | | } |
| | | //优惠券优惠金额 |
| | | couponDiscount = couponDiscount.setScale(4, RoundingMode.HALF_EVEN); |
| | | //退款金额+优惠券 |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount); |
| | | if (serviceCharge_total.compareTo(BigDecimal.ZERO) > 0) { |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount); |
| | | if (!couponAppliedUpfront) { |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | } |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setServiceCharge(serviceCharge_total.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setPaymentAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| New file |
| | |
| | | package com.ruoyi.order.controller; |
| | | |
| | | import com.ruoyi.account.api.feignClient.AppUserClient; |
| | | import com.ruoyi.order.api.query.ChargingOrderQuery; |
| | | import com.ruoyi.order.api.vo.TCharingOrderVO; |
| | | import com.ruoyi.order.service.TChargingOrderService; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertNull; |
| | | import static org.mockito.ArgumentMatchers.same; |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.verifyNoInteractions; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | | class TChargingOrderControllerTest { |
| | | |
| | | @Test |
| | | void blankPhoneDoesNotExpandQueryToAllUserIds() { |
| | | TChargingOrderService chargingOrderService = mock(TChargingOrderService.class); |
| | | AppUserClient appUserClient = mock(AppUserClient.class); |
| | | TChargingOrderController controller = new TChargingOrderController(); |
| | | ReflectionTestUtils.setField(controller, "chargingOrderService", chargingOrderService); |
| | | ReflectionTestUtils.setField(controller, "appUserClient", appUserClient); |
| | | |
| | | ChargingOrderQuery query = new ChargingOrderQuery(); |
| | | query.setPhone(""); |
| | | when(chargingOrderService.chargingOrder(same(query))).thenReturn(new TCharingOrderVO()); |
| | | |
| | | controller.chargingOrder(query); |
| | | |
| | | assertNull(query.getUserIds()); |
| | | verify(chargingOrderService).chargingOrder(same(query)); |
| | | verifyNoInteractions(appUserClient); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.order.service.impl; |
| | | |
| | | import com.ruoyi.other.api.domain.TCoupon; |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.junit.jupiter.api.Assertions.assertThrows; |
| | | |
| | | class ChargingCouponSupportTest { |
| | | |
| | | @Test |
| | | void fullReductionDiscountsThePrepaymentBeforeThirdPartyPay() { |
| | | TCoupon coupon = chargingCoupon(1); |
| | | coupon.setMeetTheConditions(new BigDecimal("3")); |
| | | coupon.setDiscountAmount(new BigDecimal("1")); |
| | | |
| | | BigDecimal discount = ChargingCouponSupport.calculateUpfrontDiscount(coupon, new BigDecimal("3")); |
| | | |
| | | assertEquals(new BigDecimal("1.00"), discount); |
| | | assertEquals(new BigDecimal("2.00"), new BigDecimal("3").subtract(discount)); |
| | | } |
| | | |
| | | @Test |
| | | void percentageDiscountHonorsMaximumDiscount() { |
| | | TCoupon coupon = chargingCoupon(2); |
| | | coupon.setMeetTheConditions(new BigDecimal("10")); |
| | | coupon.setDiscount(new BigDecimal("8")); |
| | | coupon.setMaximumDiscountAmount(new BigDecimal("1.50")); |
| | | |
| | | BigDecimal discount = ChargingCouponSupport.calculateUpfrontDiscount(coupon, new BigDecimal("10")); |
| | | |
| | | assertEquals(new BigDecimal("1.50"), discount); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsPrepaymentBelowCouponThreshold() { |
| | | TCoupon coupon = chargingCoupon(1); |
| | | coupon.setMeetTheConditions(new BigDecimal("3")); |
| | | coupon.setDiscountAmount(new BigDecimal("1")); |
| | | |
| | | IllegalArgumentException error = assertThrows(IllegalArgumentException.class, |
| | | () -> ChargingCouponSupport.calculateUpfrontDiscount(coupon, new BigDecimal("2.99"))); |
| | | |
| | | assertEquals("预付金额未达到优惠券使用门槛", error.getMessage()); |
| | | } |
| | | |
| | | private TCoupon chargingCoupon(int preferentialMode) { |
| | | TCoupon coupon = new TCoupon(); |
| | | coupon.setType(1); |
| | | coupon.setPreferentialMode(preferentialMode); |
| | | return coupon; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.order.service.impl; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | |
| | | class ChargingPaymentSupportTest { |
| | | |
| | | @Test |
| | | void classifiesPartialBalanceAsCombinationPayment() { |
| | | assertEquals(2, ChargingPaymentSupport.resolvePayMethod( |
| | | new BigDecimal("1.00"), new BigDecimal("2.00"), 3)); |
| | | } |
| | | |
| | | @Test |
| | | void classifiesZeroBalanceCombinationSelectionAsThirdPartyPayment() { |
| | | assertEquals(3, ChargingPaymentSupport.resolvePayMethod( |
| | | BigDecimal.ZERO, new BigDecimal("3.00"), 2)); |
| | | } |
| | | |
| | | @Test |
| | | void classifiesFullyCoveredPaymentAsBalancePayment() { |
| | | assertEquals(1, ChargingPaymentSupport.resolvePayMethod( |
| | | new BigDecimal("3.00"), BigDecimal.ZERO, 2)); |
| | | } |
| | | |
| | | @Test |
| | | void preservesSelectedBalanceMethodForAllFreeWhitelistPayment() { |
| | | assertEquals(1, ChargingPaymentSupport.resolvePayMethod( |
| | | BigDecimal.ZERO, BigDecimal.ZERO, 1)); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.order.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.Wrapper; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.security.service.TokenService; |
| | | import com.ruoyi.order.api.model.TChargingOrder; |
| | | import com.ruoyi.payment.api.feignClient.IcbcPaymentClient; |
| | | import com.ruoyi.payment.api.vo.IcbcPaymentResp; |
| | | import com.ruoyi.system.api.model.LoginUserApplet; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | |
| | | import java.util.Map; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.junit.jupiter.api.Assertions.assertFalse; |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | import static org.mockito.ArgumentMatchers.any; |
| | | import static org.mockito.ArgumentMatchers.eq; |
| | | import static org.mockito.Mockito.doReturn; |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.spy; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | | class TChargingOrderPaymentConfirmationTest { |
| | | private static final String ORDER_CODE = "CD202607160001"; |
| | | private static final String BANK_ORDER_ID = "231011990056000012345"; |
| | | |
| | | private TChargingOrderServiceImpl service; |
| | | private IcbcPaymentClient icbcPaymentClient; |
| | | |
| | | @BeforeEach |
| | | void setUp() { |
| | | service = spy(new TChargingOrderServiceImpl()); |
| | | TokenService tokenService = mock(TokenService.class); |
| | | icbcPaymentClient = mock(IcbcPaymentClient.class); |
| | | LoginUserApplet loginUser = new LoginUserApplet(); |
| | | loginUser.setUserId(88L); |
| | | when(tokenService.getLoginUserApplet()).thenReturn(loginUser); |
| | | ReflectionTestUtils.setField(service, "tokenService", tokenService); |
| | | ReflectionTestUtils.setField(service, "icbcPaymentClient", icbcPaymentClient); |
| | | } |
| | | |
| | | @Test |
| | | @SuppressWarnings("unchecked") |
| | | void paidBankOrderActivelyRunsBusinessCallback() { |
| | | TChargingOrder order = pendingIcbcOrder(); |
| | | doReturn(order).when(service).getOne(any(Wrapper.class)); |
| | | IcbcPaymentResp payment = bankPayment("0"); |
| | | when(icbcPaymentClient.query(ORDER_CODE)).thenReturn(R.ok(payment)); |
| | | doReturn(AjaxResult.success()).when(service) |
| | | .chargingOrderCallback(1, ORDER_CODE, BANK_ORDER_ID, "1"); |
| | | |
| | | AjaxResult result = service.confirmIcbcPayment(ORDER_CODE); |
| | | |
| | | Map<String, Object> data = (Map<String, Object>) result.get("data"); |
| | | assertEquals(200, result.get("code")); |
| | | assertTrue((Boolean) data.get("confirmed")); |
| | | verify(service).chargingOrderCallback(1, ORDER_CODE, BANK_ORDER_ID, "1"); |
| | | } |
| | | |
| | | @Test |
| | | @SuppressWarnings("unchecked") |
| | | void pendingBankOrderReturnsUnconfirmedWithoutRunningCallback() { |
| | | doReturn(pendingIcbcOrder()).when(service).getOne(any(Wrapper.class)); |
| | | when(icbcPaymentClient.query(ORDER_CODE)).thenReturn(R.ok(bankPayment("1"))); |
| | | |
| | | AjaxResult result = service.confirmIcbcPayment(ORDER_CODE); |
| | | |
| | | Map<String, Object> data = (Map<String, Object>) result.get("data"); |
| | | assertEquals(200, result.get("code")); |
| | | assertFalse((Boolean) data.get("confirmed")); |
| | | verify(service, never()).chargingOrderCallback(eq(1), eq(ORDER_CODE), any(), eq("1")); |
| | | } |
| | | |
| | | private TChargingOrder pendingIcbcOrder() { |
| | | TChargingOrder order = new TChargingOrder(); |
| | | order.setCode(ORDER_CODE); |
| | | order.setAppUserId(88L); |
| | | order.setSourceName("ICBC"); |
| | | order.setRechargePaymentStatus(1); |
| | | order.setDelFlag(false); |
| | | return order; |
| | | } |
| | | |
| | | private IcbcPaymentResp bankPayment(String payStatus) { |
| | | IcbcPaymentResp payment = new IcbcPaymentResp(); |
| | | payment.setOrderId(ORDER_CODE); |
| | | payment.setTradeNo(BANK_ORDER_ID); |
| | | payment.setStatus(payStatus); |
| | | payment.setRawResponse("{\"returnCode\":0,\"pay_status\":\"" + payStatus |
| | | + "\",\"out_trade_no\":\"" + ORDER_CODE + "\",\"order_id\":\"" + BANK_ORDER_ID + "\"}"); |
| | | return payment; |
| | | } |
| | | } |
| New file |
| | |
| | | -- 预约充电:最早可提前预约分钟数 |
| | | ALTER TABLE `t_site` |
| | | ADD COLUMN `reservation_ahead_minute` int NULL COMMENT '最早可提前预约分钟数' AFTER `reservation_end_time`; |
| New file |
| | |
| | | -- 常见问题管理缺表修复 |
| | | -- 报错示例:Object 'sys_faq' not found within 'charging_pile_other' |
| | | -- 请在 charging_pile_other 库执行;脚本可重复执行,不会删除已有数据。 |
| | | |
| | | USE `charging_pile_other`; |
| | | |
| | | CREATE TABLE IF NOT EXISTS `sys_faq` ( |
| | | `faq_id` bigint NOT NULL AUTO_INCREMENT COMMENT '问题ID', |
| | | `question` varchar(500) NOT NULL COMMENT '问题标题', |
| | | `answer` text COMMENT '问题答案', |
| | | `video_url` varchar(500) DEFAULT NULL COMMENT '视频教程URL', |
| | | `sort_order` int DEFAULT '0' COMMENT '排序号', |
| | | `status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)', |
| | | `create_by` varchar(64) DEFAULT '' COMMENT '创建者', |
| | | `create_time` datetime DEFAULT NULL COMMENT '创建时间', |
| | | `update_by` varchar(64) DEFAULT '' COMMENT '更新者', |
| | | `update_time` datetime DEFAULT NULL COMMENT '更新时间', |
| | | `remark` varchar(500) DEFAULT NULL COMMENT '备注', |
| | | `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', |
| | | PRIMARY KEY (`faq_id`), |
| | | KEY `idx_sys_faq_status_del` (`status`, `del_flag`), |
| | | KEY `idx_sys_faq_sort` (`sort_order`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='常见问题表'; |
| | | |
| | | SET @faq_video_url_col_exists := ( |
| | | SELECT COUNT(1) |
| | | FROM information_schema.COLUMNS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'sys_faq' |
| | | AND COLUMN_NAME = 'video_url' |
| | | ); |
| | | |
| | | SET @faq_add_video_url_sql := IF( |
| | | @faq_video_url_col_exists = 0, |
| | | 'ALTER TABLE `sys_faq` ADD COLUMN `video_url` varchar(500) DEFAULT NULL COMMENT ''视频教程URL'' AFTER `answer`', |
| | | 'SELECT 1' |
| | | ); |
| | | |
| | | PREPARE faq_add_video_url_stmt FROM @faq_add_video_url_sql; |
| | | EXECUTE faq_add_video_url_stmt; |
| | | DEALLOCATE PREPARE faq_add_video_url_stmt; |
| | | |
| | | INSERT INTO `sys_faq` ( |
| | | `question`, |
| | | `answer`, |
| | | `sort_order`, |
| | | `status`, |
| | | `create_by`, |
| | | `create_time`, |
| | | `del_flag` |
| | | ) |
| | | SELECT |
| | | '如何使用充电桩?', |
| | | '1. 停好车辆;2. 扫码启动充电桩;3. 连接充电枪;4. 开始充电。', |
| | | 1, |
| | | '0', |
| | | 'admin', |
| | | NOW(), |
| | | '0' |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM `sys_faq` WHERE `question` = '如何使用充电桩?' AND `del_flag` = '0' |
| | | ); |
| | | |
| | | INSERT INTO `sys_faq` ( |
| | | `question`, |
| | | `answer`, |
| | | `sort_order`, |
| | | `status`, |
| | | `create_by`, |
| | | `create_time`, |
| | | `del_flag` |
| | | ) |
| | | SELECT |
| | | '充电费用如何计算?', |
| | | '充电费用=充电电量×电价+服务费,具体以实际订单为准。', |
| | | 2, |
| | | '0', |
| | | 'admin', |
| | | NOW(), |
| | | '0' |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM `sys_faq` WHERE `question` = '充电费用如何计算?' AND `del_flag` = '0' |
| | | ); |
| | | |
| | | INSERT INTO `sys_faq` ( |
| | | `question`, |
| | | `answer`, |
| | | `sort_order`, |
| | | `status`, |
| | | `create_by`, |
| | | `create_time`, |
| | | `del_flag` |
| | | ) |
| | | SELECT |
| | | '充电过程中可以拔枪吗?', |
| | | '充电过程中请勿随意拔枪,如需停止充电请先在APP或充电桩上停止充电后再拔枪。', |
| | | 3, |
| | | '0', |
| | | 'admin', |
| | | NOW(), |
| | | '0' |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM `sys_faq` WHERE `question` = '充电过程中可以拔枪吗?' AND `del_flag` = '0' |
| | | ); |
| New file |
| | |
| | | SET @schema_name = DATABASE(); |
| | | |
| | | SET @transformer_power_exists = ( |
| | | SELECT COUNT(*) |
| | | FROM information_schema.COLUMNS |
| | | WHERE TABLE_SCHEMA = @schema_name |
| | | AND TABLE_NAME = 't_site' |
| | | AND COLUMN_NAME = 'transformer_power' |
| | | ); |
| | | SET @transformer_power_sql = IF( |
| | | @transformer_power_exists = 0, |
| | | 'ALTER TABLE `t_site` ADD COLUMN `transformer_power` decimal(12,2) NULL DEFAULT NULL COMMENT ''变压器功率(kW)'' AFTER `construction_site`', |
| | | 'SELECT 1' |
| | | ); |
| | | PREPARE transformer_power_stmt FROM @transformer_power_sql; |
| | | EXECUTE transformer_power_stmt; |
| | | DEALLOCATE PREPARE transformer_power_stmt; |
| | | |
| | | SET @member_discount_flag_exists = ( |
| | | SELECT COUNT(*) |
| | | FROM information_schema.COLUMNS |
| | | WHERE TABLE_SCHEMA = @schema_name |
| | | AND TABLE_NAME = 't_site' |
| | | AND COLUMN_NAME = 'member_discount_flag' |
| | | ); |
| | | SET @member_discount_flag_sql = IF( |
| | | @member_discount_flag_exists = 0, |
| | | 'ALTER TABLE `t_site` ADD COLUMN `member_discount_flag` tinyint NULL DEFAULT 0 COMMENT ''是否参与会员折扣(0=否,1=是)'' AFTER `transformer_power`', |
| | | 'SELECT 1' |
| | | ); |
| | | PREPARE member_discount_flag_stmt FROM @member_discount_flag_sql; |
| | | EXECUTE member_discount_flag_stmt; |
| | | DEALLOCATE PREPARE member_discount_flag_stmt; |