| New file |
| | |
| | | # Partner Appeal Site Scope Fix Implementation Plan |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** Ensure partner accounts see charging-order appeals for every site associated with their account while still hiding appeals from unrelated sites. |
| | | |
| | | **Architecture:** Reuse the partner account's site list returned by `SiteClient.getSiteListByUserId(userId)` as the single source of truth for both partner data scope and optional site filters. Keep per-record view/handle buttons controlled by the existing site-menu permission lookup. |
| | | |
| | | **Tech Stack:** Java 8, Spring Boot, MyBatis-Plus, JUnit 5, Mockito |
| | | |
| | | ## Global Constraints |
| | | |
| | | - Partner accounts may only see charging-order appeals whose charging order belongs to one of their associated sites. |
| | | - Visibility of an appeal record must not depend on whether the site has separately granted the order-appeal menu flag. |
| | | - View and handle operations continue to use the existing role and per-site permission controls. |
| | | - Platform accounts retain the existing unrestricted behavior unless a site filter is submitted. |
| | | - Do not modify unrelated workspace changes. |
| | | |
| | | --- |
| | | |
| | | ### Task 1: Restore associated-site appeal visibility |
| | | |
| | | **Files:** |
| | | - Modify: `ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderAppealController.java` |
| | | - Modify: `ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/controller/TOrderAppealControllerTest.java` |
| | | |
| | | **Interfaces:** |
| | | - Consumes: `SiteClient.getSiteListByUserId(Long userId)`. |
| | | - Produces: `getVisibleSiteMap(SysUser sysUser)` containing all sites associated with the logged-in partner. |
| | | |
| | | - [x] **Step 1: Replace the regression test that expects `PartnerClient.authSite(...ORDER_APPEAL)` with a test that expects all positive site IDs returned by `SiteClient.getSiteListByUserId(userId)`.** |
| | | - [x] **Step 2: Run `TOrderAppealControllerTest` and verify the old implementation fails the new expectation.** |
| | | - [x] **Step 3: Load the visible-site map for every partner request and use its key set as the partner appeal data scope.** |
| | | - [x] **Step 4: Remove the `SiteMenu.ORDER_APPEAL` data-scope lookup while retaining `managePageListMenu` for view/handle flags.** |
| | | - [x] **Step 5: Run the focused order-module test and compile dependent modules.** |
| | | - [x] **Step 6: Run `git diff --check` and review the final diff for unrelated changes.** |
| | |
| | | ChargeMonitoring chargeMonitoring = chargingPileService.chargeMonitoring(siteId); |
| | | return AjaxResult.success(chargeMonitoring); |
| | | } |
| | | |
| | | @RequiresPermissions(value = {"/chargingEquipmentMonitoring"}, logical = Logical.OR) |
| | | @ResponseBody |
| | | @GetMapping("/getChargingMonitoringSiteList") |
| | | @ApiOperation(value = "获取当前账号允许监控的站点", tags = {"管理后台-充电设备监控"}) |
| | | public AjaxResult<List<Site>> getChargingMonitoringSiteList() { |
| | | return AjaxResult.success(chargingPileService.getChargingMonitoringSiteList()); |
| | | } |
| | | |
| | | |
| | | @RequiresPermissions(value = {"/chargingEquipmentMonitoring", "/generalBigScreen", "/dataCockpit"}, logical = Logical.OR) |
| | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.chargingPile.api.dto.PageChargingPileListDTO; |
| | | import com.ruoyi.chargingPile.api.model.TChargingPile; |
| | | import com.ruoyi.chargingPile.api.model.Site; |
| | | import com.ruoyi.chargingPile.api.query.BatchSetAccountingStrategy; |
| | | import com.ruoyi.chargingPile.api.query.PageChargingPileList; |
| | | import com.ruoyi.chargingPile.api.vo.TChargingPileVO; |
| | |
| | | */ |
| | | ChargeMonitoring chargeMonitoring(Integer siteId); |
| | | |
| | | List<Site> getChargingMonitoringSiteList(); |
| | | |
| | | |
| | | /** |
| | | * 获取充电枪各种状态汇总 |
| | |
| | | * @return |
| | | */ |
| | | @Override |
| | | public ChargeMonitoring chargeMonitoring(Integer siteId) { |
| | | //获取当前登录账户的站点权限数据 |
| | | Set<Integer> siteIds = new HashSet<>(); |
| | | if(0 == siteId){ |
| | | SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); |
| | | Integer roleType = sysUser.getRoleType(); |
| | | Integer objectId = sysUser.getObjectId(); |
| | | if(2 == roleType){ |
| | | siteIds = partnerService.authSite(objectId, SiteMenu.CHARGING_FACILITY_MONITORING); |
| | | }else{ |
| | | //非管理员需要根据角色和用户配置查询允许的站点数据 |
| | | if(!SecurityUtils.isAdmin(tokenService.getLoginUser().getUserid())){ |
| | | List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData(); |
| | | List<SysUserRoleVo> data2 = sysUserRoleClient.getRoleByUserId(sysUser.getUserId()).getData(); |
| | | List<Integer> data1 = roleSiteClient.getSiteIds(data2.get(0).getRoleId()).getData(); |
| | | data.addAll(data1); |
| | | siteIds = new HashSet<>(data); |
| | | }else{ |
| | | siteIds = null; |
| | | public List<Site> getChargingMonitoringSiteList() { |
| | | Set<Integer> siteIds = resolveChargingMonitoringSiteIds(0); |
| | | return siteService.list(new LambdaQueryWrapper<Site>() |
| | | .in(Site::getId, siteIds) |
| | | .eq(Site::getDelFlag, 0) |
| | | .orderByAsc(Site::getId)); |
| | | } |
| | | |
| | | private Set<Integer> resolveChargingMonitoringSiteIds(Integer requestedSiteId) { |
| | | Long userId = tokenService.getLoginUser().getUserid(); |
| | | Set<Integer> allowedSiteIds = new HashSet<>(); |
| | | if (SecurityUtils.isAdmin(userId)) { |
| | | allowedSiteIds.addAll(siteService.list(new LambdaQueryWrapper<Site>() |
| | | .eq(Site::getDelFlag, 0)) |
| | | .stream().map(Site::getId).collect(Collectors.toSet())); |
| | | return filterRequestedMonitoringSiteIds(allowedSiteIds, requestedSiteId); |
| | | } |
| | | |
| | | R<SysUser> userResult = sysUserClient.getSysUser(userId); |
| | | SysUser sysUser = userResult == null ? null : userResult.getData(); |
| | | if (sysUser == null) { |
| | | return Collections.singleton(-1); |
| | | } |
| | | if (Integer.valueOf(2).equals(sysUser.getRoleType())) { |
| | | Set<Integer> partnerSiteIds = partnerService.authSite( |
| | | sysUser.getObjectId(), SiteMenu.CHARGING_FACILITY_MONITORING); |
| | | if (partnerSiteIds != null) { |
| | | allowedSiteIds.addAll(partnerSiteIds); |
| | | } |
| | | } else { |
| | | R<List<Integer>> userSiteResult = userSiteClient.getSiteIds(userId); |
| | | if (userSiteResult != null && userSiteResult.getData() != null) { |
| | | allowedSiteIds.addAll(userSiteResult.getData()); |
| | | } |
| | | R<List<SysUserRoleVo>> roleResult = sysUserRoleClient.getRoleByUserId(userId); |
| | | List<SysUserRoleVo> roles = roleResult == null ? null : roleResult.getData(); |
| | | if (roles != null) { |
| | | for (SysUserRoleVo role : roles) { |
| | | if (role == null || role.getRoleId() == null) { |
| | | continue; |
| | | } |
| | | R<List<Integer>> roleSiteResult = roleSiteClient.getSiteIds(role.getRoleId()); |
| | | if (roleSiteResult != null && roleSiteResult.getData() != null) { |
| | | allowedSiteIds.addAll(roleSiteResult.getData()); |
| | | } |
| | | } |
| | | } |
| | | }else{ |
| | | siteIds.add(siteId); |
| | | } |
| | | List<Site> sites = null; |
| | | if(null == siteIds){ |
| | | sites = siteService.list(new LambdaQueryWrapper<Site>().eq(Site::getDelFlag, 0)); |
| | | siteIds = sites.stream().map(Site::getId).collect(Collectors.toSet()); |
| | | }else{ |
| | | sites = siteService.listByIds(siteIds); |
| | | return filterRequestedMonitoringSiteIds(allowedSiteIds, requestedSiteId); |
| | | } |
| | | |
| | | static Set<Integer> filterRequestedMonitoringSiteIds(Set<Integer> allowedSiteIds, Integer requestedSiteId) { |
| | | Set<Integer> allowed = allowedSiteIds == null ? Collections.emptySet() : allowedSiteIds; |
| | | if (requestedSiteId != null && requestedSiteId > 0) { |
| | | return allowed.contains(requestedSiteId) |
| | | ? Collections.singleton(requestedSiteId) : Collections.singleton(-1); |
| | | } |
| | | return allowed.isEmpty() ? Collections.singleton(-1) : new HashSet<>(allowed); |
| | | } |
| | | |
| | | @Override |
| | | public ChargeMonitoring chargeMonitoring(Integer siteId) { |
| | | Set<Integer> siteIds = resolveChargingMonitoringSiteIds(siteId); |
| | | List<Site> sites = siteService.list(new LambdaQueryWrapper<Site>() |
| | | .in(Site::getId, siteIds) |
| | | .eq(Site::getDelFlag, 0)); |
| | | List<TChargingPile> piles = this.list(new LambdaQueryWrapper<TChargingPile>() |
| | | .in(TChargingPile::getSiteId, siteIds) |
| | | .eq(TChargingPile::getDelFlag, 0)); |
| | |
| | | */ |
| | | @Override |
| | | public ChargingGunCountMonitoring getChargingGunCountMonitoring(Integer siteId) { |
| | | //获取当前登录账户的站点权限数据 |
| | | Set<Integer> siteIds = new HashSet<>(); |
| | | if(0 == siteId){ |
| | | SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); |
| | | Integer roleType = sysUser.getRoleType(); |
| | | Integer objectId = sysUser.getObjectId(); |
| | | if(2 == roleType){ |
| | | siteIds = partnerService.authSite(objectId, SiteMenu.CHARGING_FACILITY_MONITORING); |
| | | }else{ |
| | | //非管理员需要根据角色和用户配置查询允许的站点数据 |
| | | if(!SecurityUtils.isAdmin(tokenService.getLoginUser().getUserid())){ |
| | | List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData(); |
| | | List<SysUserRoleVo> data2 = sysUserRoleClient.getRoleByUserId(sysUser.getUserId()).getData(); |
| | | List<Integer> data1 = roleSiteClient.getSiteIds(data2.get(0).getRoleId()).getData(); |
| | | data.addAll(data1); |
| | | siteIds = new HashSet<>(data); |
| | | }else{ |
| | | List<Site> list = siteService.list(new LambdaQueryWrapper<Site>().eq(Site::getDelFlag, 0)); |
| | | siteIds = list.stream().map(Site::getId).collect(Collectors.toSet()); |
| | | } |
| | | } |
| | | }else{ |
| | | siteIds.add(siteId); |
| | | } |
| | | Set<Integer> siteIds = resolveChargingMonitoringSiteIds(siteId); |
| | | List<TChargingGun> list = chargingGunService.list(new LambdaQueryWrapper<TChargingGun>().in(TChargingGun::getSiteId, siteIds).eq(TChargingGun::getDelFlag, 0)); |
| | | //空闲 |
| | | long leisure = list.stream().filter(s -> s.getStatus().equals(2)).count(); |
| | |
| | | */ |
| | | @Override |
| | | public PageInfo<ChargingGunMonitoring> getChargingGunMonitoring(GetChargingGunMonitoring query) { |
| | | //获取当前登录账户的站点权限数据 |
| | | Set<Integer> siteIds = new HashSet<>(); |
| | | if(0 == query.getSiteId()){ |
| | | SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); |
| | | R<Integer> admin = sysUserClient.isAdmin(tokenService.getLoginUser().getUserid()); |
| | | Integer roleType = sysUser.getRoleType(); |
| | | Integer objectId = sysUser.getObjectId(); |
| | | if(2 == roleType){ |
| | | siteIds = partnerService.authSite(objectId, SiteMenu.CHARGING_FACILITY_MONITORING); |
| | | }else{ |
| | | //非管理员需要根据角色和用户配置查询允许的站点数据 |
| | | if(admin.getData()!=1){ |
| | | List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData(); |
| | | List<SysUserRoleVo> data2 = sysUserRoleClient.getRoleByUserId(sysUser.getUserId()).getData(); |
| | | List<Integer> data1 = roleSiteClient.getSiteIds(data2.get(0).getRoleId()).getData(); |
| | | data.addAll(data1); |
| | | siteIds = new HashSet<>(data); |
| | | }else{ |
| | | List<Site> list = siteService.list(new LambdaQueryWrapper<Site>().eq(Site::getDelFlag, 0)); |
| | | siteIds = list.stream().map(Site::getId).collect(Collectors.toSet()); |
| | | } |
| | | } |
| | | }else{ |
| | | siteIds.add(query.getSiteId()); |
| | | } |
| | | Set<Integer> siteIds = resolveChargingMonitoringSiteIds(query.getSiteId()); |
| | | |
| | | if(null != query.getStatus() && 0 < query.getStatus().size()){ |
| | | if(query.getStatus().contains(3)){ |
| New file |
| | |
| | | package com.ruoyi.chargingPile.service.impl; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.HashSet; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | |
| | | class TChargingPileMonitoringPermissionTest { |
| | | |
| | | @Test |
| | | void keepsAllAuthorizedSitesWhenNoSiteIsSelected() { |
| | | assertEquals(new HashSet<>(Arrays.asList(10, 20)), |
| | | TChargingPileServiceImpl.filterRequestedMonitoringSiteIds( |
| | | new HashSet<>(Arrays.asList(10, 20)), 0)); |
| | | } |
| | | |
| | | @Test |
| | | void allowsOnlyAnAuthorizedRequestedSite() { |
| | | assertEquals(Collections.singleton(20), |
| | | TChargingPileServiceImpl.filterRequestedMonitoringSiteIds( |
| | | new HashSet<>(Arrays.asList(10, 20)), 20)); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsUnauthorizedOrEmptySiteScopes() { |
| | | assertEquals(Collections.singleton(-1), |
| | | TChargingPileServiceImpl.filterRequestedMonitoringSiteIds( |
| | | new HashSet<>(Arrays.asList(10, 20)), 30)); |
| | | assertEquals(Collections.singleton(-1), |
| | | TChargingPileServiceImpl.filterRequestedMonitoringSiteIds(Collections.emptySet(), 0)); |
| | | } |
| | | } |
| | |
| | | taskUtil.refreshChargingSettlementBill(targetMonth.atDay(1)); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @RequiresPermissions(value = {"/accountSettlementStatement"}, logical = Logical.OR) |
| | | @PostMapping(value = "/generateAccount") |
| | | @ApiOperation(value = "主动生成指定月份未出账账户结算账单", tags = {"管理后台-账户结算账单"}) |
| | | public R generateAccountSettlementBill(@RequestParam(value = "billingMonth", required = false) String billingMonth) { |
| | | YearMonth targetMonth = billingMonth != null && !billingMonth.trim().isEmpty() |
| | | ? YearMonth.parse(billingMonth.trim()) : YearMonth.now(); |
| | | if (targetMonth.isAfter(YearMonth.now())) { |
| | | return R.fail("不能生成未来月份账单"); |
| | | } |
| | | taskUtil.refreshAccountSettlementStatement(targetMonth.atDay(1)); |
| | | return R.ok(); |
| | | } |
| | | @GetMapping(value = "/test") |
| | | public R accountAdd() { |
| | | // List<TChargingBill> list = tChargingBillService.lambdaQuery().list(); |
| | |
| | | tChargingOrderExport.setRechargePaymentStatus(chargingOrderListVO.getRechargePaymentStatus()+""); |
| | | tChargingOrderExport.setType("充电订单"); |
| | | tChargingOrderExport.setChargingType("单桩双充"); |
| | | tChargingOrderExport.setEndmode(chargingOrderListVO.getEndMode()+""); |
| | | tChargingOrderExport.setEndmode(formatChargingEndMode(chargingOrderListVO.getEndMode())); |
| | | tChargingOrderExport.setChargingEndAccount("平台"); |
| | | tChargingOrderExport.setIsFree("否"); |
| | | tChargingOrderExport.setElectrovalence(chargingOrderListVO.getElectrovalence()+""); |
| | |
| | | .stripTrailingZeros() |
| | | .toPlainString(); |
| | | } |
| | | |
| | | static String formatChargingEndMode(Integer endMode) { |
| | | return endMode == null ? "" : endMode.toString(); |
| | | } |
| | | |
| | | |
| | | |
| | |
| | | import com.ruoyi.account.api.feignClient.AppUserClient; |
| | | import com.ruoyi.account.api.model.TAppUser; |
| | | import com.ruoyi.chargingPile.api.dto.GetSiteListDTO; |
| | | import com.ruoyi.chargingPile.api.domain.SiteMenu; |
| | | import com.ruoyi.chargingPile.api.feignClient.PartnerClient; |
| | | import com.ruoyi.chargingPile.api.feignClient.SiteClient; |
| | | import com.ruoyi.common.core.domain.R; |
| | |
| | | SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); |
| | | Integer roleType = sysUser.getRoleType(); |
| | | boolean partnerUser = Objects.equals(roleType, 2); |
| | | Map<Integer, GetSiteListDTO> visibleSiteMap = hasSiteFilter ? getVisibleSiteMap(sysUser) : Collections.emptyMap(); |
| | | Set<Integer> partnerSiteIds = partnerUser ? getPartnerAppealSiteIds(sysUser) : Collections.emptySet(); |
| | | Map<Integer, GetSiteListDTO> visibleSiteMap = partnerUser || hasSiteFilter |
| | | ? getVisibleSiteMap(sysUser) |
| | | : Collections.emptyMap(); |
| | | Set<Integer> partnerSiteIds = partnerUser ? visibleSiteMap.keySet() : Collections.emptySet(); |
| | | List<TOrderAppeal> res = new ArrayList<>(); |
| | | for (TOrderAppeal record : page.getRecords()) { |
| | | record.setUid(record.getId() + ""); |
| | |
| | | return R.ok(page); |
| | | } |
| | | |
| | | Set<Integer> getPartnerAppealSiteIds(SysUser sysUser) { |
| | | if (sysUser == null || sysUser.getObjectId() == null) { |
| | | return Collections.emptySet(); |
| | | } |
| | | R<Set<Integer>> result = partnerClient.authSite(sysUser.getObjectId(), SiteMenu.ORDER_APPEAL.getValue()); |
| | | if (result == null || result.getData() == null) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return result.getData().stream() |
| | | .filter(Objects::nonNull) |
| | | .filter(siteId -> siteId > 0) |
| | | .collect(java.util.stream.Collectors.toSet()); |
| | | } |
| | | |
| | | private Map<Integer, GetSiteListDTO> getVisibleSiteMap(SysUser sysUser) { |
| | | Map<Integer, GetSiteListDTO> getVisibleSiteMap(SysUser sysUser) { |
| | | if (sysUser == null || sysUser.getUserId() == null) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<GetSiteListDTO> data = siteClient.getSiteListByUserId(sysUser.getUserId()).getData(); |
| | | R<List<GetSiteListDTO>> result = siteClient.getSiteListByUserId(sysUser.getUserId()); |
| | | Map<Integer, GetSiteListDTO> siteMap = new HashMap<>(); |
| | | if (result == null || result.getData() == null) { |
| | | return siteMap; |
| | | } |
| | | List<GetSiteListDTO> data = result.getData(); |
| | | if (data != null) { |
| | | for (GetSiteListDTO datum : data) { |
| | | if (datum.getId() != null) { |
| | | if (datum != null && datum.getId() != null && datum.getId() > 0) { |
| | | siteMap.put(datum.getId(), datum); |
| | | } |
| | | } |
| | |
| | | * 添加账户结算账单数据 |
| | | */ |
| | | public void addAccountSettlementStatement(){ |
| | | int day = LocalDate.now().getDayOfMonth(); |
| | | int lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | //时间在当月的2号到当月的最后一天才执行汇总数据和生成新的汇总数据(中间有修改数据的情况) |
| | | if (2 <= day && day <= lastDay) { |
| | | refreshAccountSettlementStatement(LocalDate.now().minusMonths(1)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 主动刷新指定月份的账户结算账单。只重建该月未出账数据,已出账数据不受影响。 |
| | | */ |
| | | public synchronized void refreshAccountSettlementStatement(LocalDate billingMonth) { |
| | | try { |
| | | int day = LocalDate.now().getDayOfMonth(); |
| | | int lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | //时间在当月的2号到当月的最后一天才执行汇总数据和生成新的汇总数据(中间有修改数据的情况) |
| | | if(2 <= day && day <= lastDay){ |
| | | // 获取上个月的开始和结束日期 |
| | | LocalDate firstDayOfLastMonth = LocalDate.now().minusMonths(1). |
| | | withDayOfMonth(2); |
| | | String string = firstDayOfLastMonth.toString(); |
| | | if (billingMonth == null) { |
| | | throw new IllegalArgumentException("账单月份不能为空"); |
| | | } |
| | | LocalDate firstDayOfBillingMonth = billingMonth.withDayOfMonth(2); |
| | | String string = firstDayOfBillingMonth.toString(); |
| | | // 将-替换为空字符串 |
| | | string = string.replace("-", ""); |
| | | |
| | | //上个月 |
| | | String lastMonth = LocalDate.now().minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | String targetMonth = billingMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | //统计数据的开始时间 |
| | | String startTime = lastMonth + "-01 00:00:00"; |
| | | String startTime = targetMonth + "-01 00:00:00"; |
| | | //统计数据的结束时间 |
| | | int lasDay = LocalDate.now().minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | String endTime = lastMonth + "-" + String.format("%02d", lasDay) + " 23:59:59"; |
| | | LocalDateTime createTime = LocalDateTime.parse(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")) + "-02 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | int targetLastDay = billingMonth.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | String endTime = targetMonth + "-" + String.format("%02d", targetLastDay) + " 23:59:59"; |
| | | LocalDateTime createTime = LocalDateTime.now(); |
| | | |
| | | //删除之前生成的汇总数据,在添加最新的汇总数据 |
| | | String format = firstDayOfLastMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | String format = firstDayOfBillingMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | accountSettlementStatementService.remove(new QueryWrapper<TAccountSettlementStatement>().eq("status", 1).last(" and DATE_FORMAT(billing_cycle, '%Y-%m') = '" + format + "'")); |
| | | |
| | | Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap(); |
| | |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | accountSettlementStatement.setSiteId(0); |
| | | accountSettlementStatement.setAccountType(i); |
| | | accountSettlementStatement.setBillingCycle(firstDayOfLastMonth); |
| | | accountSettlementStatement.setBillingCycle(firstDayOfBillingMonth); |
| | | accountSettlementStatement.setStatus(1); |
| | | accountSettlementStatement.setCode("JSD"+string+randomDigits); |
| | | |
| | |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | accountSettlementStatement1.setSiteId(siteId); |
| | | accountSettlementStatement1.setAccountType(i); |
| | | accountSettlementStatement1.setBillingCycle(firstDayOfLastMonth); |
| | | accountSettlementStatement1.setBillingCycle(firstDayOfBillingMonth); |
| | | accountSettlementStatement1.setStatus(1); |
| | | accountSettlementStatement1.setCode("JSD"+string+randomDigits1); |
| | | accountSettlementStatement1.setRechargeAmount(recharge_amount.setScale(2, RoundingMode.HALF_EVEN)); |
| | |
| | | accountSettlementStatementService.save(accountSettlementStatement); |
| | | accountSettlementStatementService.saveBatch(list); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | log.error("生成{}账户结算账单失败", billingMonth, e); |
| | | throw new RuntimeException("生成账户结算账单失败", e); |
| | | } |
| | | } |
| | | |
| | |
| | | <select id="chargingOrderShareIncomeByOrderIds" resultType="map"> |
| | | select |
| | | s.charging_order_id as orderId, |
| | | sum( |
| | | IFNULL(s.channel_pay_amount, 0) |
| | | - IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END) |
| | | - IFNULL(s.share_amount, 0) |
| | | * (IFNULL(s.channel_pay_amount, 0) |
| | | - IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END)) |
| | | / IFNULL(s.channel_pay_amount, 0) |
| | | ) as platformIncome, |
| | | sum( |
| | | IFNULL(s.share_amount, 0) |
| | | * (IFNULL(s.channel_pay_amount, 0) |
| | | - IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END)) |
| | | / IFNULL(s.channel_pay_amount, 0) |
| | | ) as partnerIncome |
| | | sum(IFNULL(s.channel_pay_amount, 0) - IFNULL(s.share_amount, 0)) as platformIncome, |
| | | sum(IFNULL(s.share_amount, 0)) as partnerIncome |
| | | from `charging_pile_order`.t_charging_order_share_record s |
| | | inner join `charging_pile_order`.t_charging_order o on o.id = s.charging_order_id |
| | | where s.del_flag = 0 and s.charging_order_id is not null |
| | | and IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END) |
| | | < IFNULL(s.channel_pay_amount, 0) |
| | | <if test="orderIds != null and orderIds.size()>0"> |
| | | and s.charging_order_id in |
| | | <foreach collection="orderIds" item="item" index="index" separator="," open="(" close=")"> |
| | |
| | | <select id="chargingOrderShareIncomeByPayChannel" resultType="map"> |
| | | select |
| | | s.pay_channel as payChannel, |
| | | sum( |
| | | IFNULL(s.share_amount, 0) |
| | | * (IFNULL(s.channel_pay_amount, 0) |
| | | - IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END)) |
| | | / IFNULL(s.channel_pay_amount, 0) |
| | | ) as shareAmount |
| | | sum(IFNULL(s.share_amount, 0)) as shareAmount |
| | | from t_charging_order_share_record s |
| | | inner join t_charging_order o on s.charging_order_id = o.id |
| | | where s.del_flag = 0 |
| | |
| | | and o.del_flag = 0 |
| | | and o.recharge_payment_status = 2 |
| | | and o.status = 5 |
| | | and IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END) |
| | | < IFNULL(s.channel_pay_amount, 0) |
| | | <if test="sixBefore != null"> |
| | | and o.create_time >= #{sixBefore} |
| | | </if> |
| | |
| | | |
| | | <select id="chargingOrderShareIncomeSummaryByOrderIds" resultType="map"> |
| | | select |
| | | sum( |
| | | IFNULL(s.channel_pay_amount, 0) |
| | | - IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END) |
| | | - IFNULL(s.share_amount, 0) |
| | | * (IFNULL(s.channel_pay_amount, 0) |
| | | - IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END)) |
| | | / IFNULL(s.channel_pay_amount, 0) |
| | | ) as platformIncomeTotal, |
| | | sum( |
| | | IFNULL(s.share_amount, 0) |
| | | * (IFNULL(s.channel_pay_amount, 0) |
| | | - IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END)) |
| | | / IFNULL(s.channel_pay_amount, 0) |
| | | ) as partnerIncomeTotal |
| | | sum(IFNULL(s.channel_pay_amount, 0) - IFNULL(s.share_amount, 0)) as platformIncomeTotal, |
| | | sum(IFNULL(s.share_amount, 0)) as partnerIncomeTotal |
| | | from `charging_pile_order`.t_charging_order_share_record s |
| | | inner join `charging_pile_order`.t_charging_order o on o.id = s.charging_order_id |
| | | where s.del_flag = 0 and s.charging_order_id is not null |
| | | and IFNULL(o.third_refund_amount, CASE WHEN o.pay_method = 3 THEN IFNULL(o.refund_amount, 0) ELSE 0 END) |
| | | < IFNULL(s.channel_pay_amount, 0) |
| | | <if test="orderIds != null and orderIds.size()>0"> |
| | | and s.charging_order_id in |
| | | <foreach collection="orderIds" item="item" index="index" separator="," open="(" close=")"> |
| | |
| | | } |
| | | |
| | | @Test |
| | | void shouldFormatNullChargingEndModeAsBlank() { |
| | | assertEquals("", FinancialSettlementController.formatChargingEndMode(null)); |
| | | assertEquals("0", FinancialSettlementController.formatChargingEndMode(0)); |
| | | assertEquals("3", FinancialSettlementController.formatChargingEndMode(3)); |
| | | } |
| | | |
| | | @Test |
| | | void shouldBuildChargingPeriodExportSummaryFromExportRows() { |
| | | ChargingOrderListVO first = new ChargingOrderListVO(); |
| | | first.setElectrovalence(new BigDecimal("10.12")); |
| | |
| | | package com.ruoyi.order.controller; |
| | | |
| | | import com.ruoyi.chargingPile.api.domain.SiteMenu; |
| | | import com.ruoyi.chargingPile.api.feignClient.PartnerClient; |
| | | import com.ruoyi.chargingPile.api.dto.GetSiteListDTO; |
| | | import com.ruoyi.chargingPile.api.feignClient.SiteClient; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.security.service.TokenService; |
| | | import com.ruoyi.order.service.TOrderAppealService; |
| | |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.HashSet; |
| | | import java.util.Set; |
| | | import java.util.Map; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.mockito.Mockito.mock; |
| | |
| | | class TOrderAppealControllerTest { |
| | | |
| | | @Test |
| | | void partnerAppealSitesUseOrderAppealPermissionInsteadOfSiteListPermission() { |
| | | void partnerAppealSitesUseAllSitesAssociatedWithCurrentAccount() { |
| | | TOrderAppealService appealService = mock(TOrderAppealService.class); |
| | | TokenService tokenService = mock(TokenService.class); |
| | | PartnerClient partnerClient = mock(PartnerClient.class); |
| | | SiteClient siteClient = mock(SiteClient.class); |
| | | TOrderAppealController controller = new TOrderAppealController(appealService, tokenService); |
| | | ReflectionTestUtils.setField(controller, "partnerClient", partnerClient); |
| | | ReflectionTestUtils.setField(controller, "siteClient", siteClient); |
| | | |
| | | SysUser partnerUser = new SysUser(); |
| | | partnerUser.setUserId(7L); |
| | | partnerUser.setObjectId(9); |
| | | when(partnerClient.authSite(9, SiteMenu.ORDER_APPEAL.getValue())) |
| | | .thenReturn(R.ok(new HashSet<>(Arrays.asList(-1, 11, 12)))); |
| | | when(siteClient.getSiteListByUserId(7L)) |
| | | .thenReturn(R.ok(Arrays.asList(site(-1), site(11), null, site(12)))); |
| | | |
| | | Set<Integer> result = controller.getPartnerAppealSiteIds(partnerUser); |
| | | Map<Integer, GetSiteListDTO> result = controller.getVisibleSiteMap(partnerUser); |
| | | |
| | | assertEquals(new HashSet<>(Arrays.asList(11, 12)), result); |
| | | verify(partnerClient).authSite(9, SiteMenu.ORDER_APPEAL.getValue()); |
| | | assertEquals(new HashSet<>(Arrays.asList(11, 12)), result.keySet()); |
| | | verify(siteClient).getSiteListByUserId(7L); |
| | | } |
| | | |
| | | private GetSiteListDTO site(Integer id) { |
| | | GetSiteListDTO site = new GetSiteListDTO(); |
| | | site.setId(id); |
| | | return site; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.order.mapper; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.io.InputStreamReader; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertFalse; |
| | | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | |
| | | class ChargingOrderShareIncomeMapperTest { |
| | | |
| | | @Test |
| | | void usesNetShareRecordAmountsWithoutDeductingOrderRefundAgain() throws IOException { |
| | | String mapper = resource("mapper/order/TChargingOrderMapper.xml"); |
| | | String detailQuery = select(mapper, "chargingOrderShareIncomeByOrderIds"); |
| | | String channelQuery = select(mapper, "chargingOrderShareIncomeByPayChannel"); |
| | | String summaryQuery = select(mapper, "chargingOrderShareIncomeSummaryByOrderIds"); |
| | | |
| | | assertTrue(detailQuery.contains("sum(IFNULL(s.share_amount, 0)) as partnerIncome")); |
| | | assertTrue(detailQuery.contains("sum(IFNULL(s.channel_pay_amount, 0) - IFNULL(s.share_amount, 0)) as platformIncome")); |
| | | assertTrue(channelQuery.contains("sum(IFNULL(s.share_amount, 0)) as shareAmount")); |
| | | assertTrue(summaryQuery.contains("sum(IFNULL(s.share_amount, 0)) as partnerIncomeTotal")); |
| | | assertTrue(summaryQuery.contains("sum(IFNULL(s.channel_pay_amount, 0) - IFNULL(s.share_amount, 0)) as platformIncomeTotal")); |
| | | |
| | | assertFalse(detailQuery.contains("third_refund_amount")); |
| | | assertFalse(channelQuery.contains("third_refund_amount")); |
| | | assertFalse(summaryQuery.contains("third_refund_amount")); |
| | | } |
| | | |
| | | private String select(String mapper, String id) { |
| | | int start = mapper.indexOf("<select id=\"" + id + "\""); |
| | | int end = mapper.indexOf("</select>", start); |
| | | assertTrue(start >= 0 && end > start); |
| | | return mapper.substring(start, end); |
| | | } |
| | | |
| | | private String resource(String path) throws IOException { |
| | | InputStream input = getClass().getClassLoader().getResourceAsStream(path); |
| | | assertNotNull(input); |
| | | try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { |
| | | return reader.lines().collect(Collectors.joining("\n")); |
| | | } |
| | | } |
| | | } |