diff --git a/sql/upgrade/1.0.10/upgrade.sql b/sql/upgrade/1.0.10/upgrade.sql new file mode 100644 index 0000000..d8bb285 --- /dev/null +++ b/sql/upgrade/1.0.10/upgrade.sql @@ -0,0 +1,13 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +ALTER TABLE `wfc_user_db`.`u_cdr_history` +ADD COLUMN `use_type` tinyint(4) NULL COMMENT 'use id' AFTER `duration`, +ADD COLUMN `use_id` bigint(20) NULL COMMENT 'use type' AFTER `use_type` + +ALTER TABLE `wfc_user_db`.`u_client` +ADD COLUMN `active` tinyint(1) NULL COMMENT 'Whether the client is online' AFTER `client_mac`, +ADD COLUMN `use_type` tinyint(4) NULL COMMENT 'use type' AFTER `active`, +ADD COLUMN `use_id` bigint(20) NULL COMMENT 'use id' AFTER `use_type` + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/sql/wfc_user_db/wfc_user_db.sql b/sql/wfc_user_db/wfc_user_db.sql index 6577200..158f5ba 100644 --- a/sql/wfc_user_db/wfc_user_db.sql +++ b/sql/wfc_user_db/wfc_user_db.sql @@ -188,6 +188,9 @@ CREATE TABLE `u_cdr_history` ( `start_time` bigint(20) NULL DEFAULT NULL COMMENT 'Start time', `end_time` bigint(20) NULL DEFAULT NULL COMMENT 'End time', `duration` bigint(20) NULL DEFAULT NULL COMMENT 'Duration(s)', + `active` tinyint(1) DEFAULT NULL COMMENT 'Whether the client is online', + `use_type` tinyint(4) DEFAULT NULL COMMENT 'use type', + `use_id` bigint(20) DEFAULT NULL COMMENT 'use id', `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '0' COMMENT 'delete flag', `create_by` bigint(20) NULL DEFAULT NULL COMMENT 'creater', `create_time` datetime NULL DEFAULT NULL COMMENT 'create time', @@ -211,6 +214,8 @@ CREATE TABLE `u_client` ( `client_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Client Name', `client_device_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Client device type', `client_mac` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Client mac address', + `use_type` tinyint(4) DEFAULT NULL COMMENT 'use type', + `use_id` bigint(20) DEFAULT NULL COMMENT 'use id', `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '0' COMMENT 'delete flag', `create_by` bigint(20) NULL DEFAULT NULL COMMENT 'creater', `create_time` datetime NULL DEFAULT NULL COMMENT 'create time', diff --git a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/client/OmadaClientInsightApi.java b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/client/OmadaClientInsightApi.java index 8e4620a..502c38c 100644 --- a/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/client/OmadaClientInsightApi.java +++ b/wfc-api/wfc-api-omada/src/main/java/org/wfc/omada/api/client/OmadaClientInsightApi.java @@ -50,6 +50,6 @@ public interface OmadaClientInsightApi { @RequestMapping(value = "/openapi/v1/${omada.omadac-id}/sites/{siteId}/insight/past-connection", produces = "*/*", method = RequestMethod.GET) - ResponseEntity getGridPastConnections(@ApiParam(value = "Site ID",required=true) @PathVariable("siteId") String siteId,@NotNull @ApiParam(value = "Start page number. Start from 1.", required = true) @Valid @RequestParam(value = "page", required = true) Integer page,@NotNull @ApiParam(value = "Number of entries per page. It should be within the range of 1–1000.", required = true) @Valid @RequestParam(value = "pageSize", required = true) Integer pageSize,@ApiParam(value = "Fuzzy query parameters, support field name,mac,ssid.") @Valid @RequestParam(value = "searchKey", required = false) String searchKey); + ResponseEntity getGridPastConnections(@ApiParam(value = "Site ID",required=true) @PathVariable("siteId") String siteId,@NotNull @ApiParam(value = "Start page number. Start from 1.", required = true) @Valid @RequestParam(value = "page", required = true) Integer page,@NotNull @ApiParam(value = "Number of entries per page. It should be within the range of 1–1000.", required = true) @Valid @RequestParam(value = "pageSize", required = true) Integer pageSize,@Valid @RequestParam(value = "sorts.lastSeen", required = false) String sortsLastSeen,@ApiParam(value = "Filter query parameters, support field time range: start timestamp (ms).") @Valid @RequestParam(value = "filters.timeStart", required = false) String filtersTimeStart,@ApiParam(value = "Filter query parameters, support field time range: end timestamp (ms).") @Valid @RequestParam(value = "filters.timeEnd", required = false) String filtersTimeEnd, @ApiParam(value = "Fuzzy query parameters, support field name,mac,ssid.") @Valid @RequestParam(value = "searchKey", required = false) String searchKey); } diff --git a/wfc-common/wfc-common-core/src/main/java/org/wfc/common/core/utils/ResponseUtils.java b/wfc-common/wfc-common-core/src/main/java/org/wfc/common/core/utils/ResponseUtils.java index 677c0c8..63864ed 100644 --- a/wfc-common/wfc-common-core/src/main/java/org/wfc/common/core/utils/ResponseUtils.java +++ b/wfc-common/wfc-common-core/src/main/java/org/wfc/common/core/utils/ResponseUtils.java @@ -24,4 +24,12 @@ public class ResponseUtils { log.error("Omada error msg: {}", msg); } } + + public static boolean isSuccessResponse(Integer errorCode, String msg) { + if (errorCode != WifiConstants.ERROR_CODE_SUCCESS) { + log.error("Omada error msg: {}", msg); + return false; + } + return true; + } } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/api/omada/OmadaWifiApi.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/api/omada/OmadaWifiApi.java index 887fc8e..e785931 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/api/omada/OmadaWifiApi.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/api/omada/OmadaWifiApi.java @@ -7,9 +7,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.wfc.common.core.constant.WifiConstants; +import org.wfc.common.core.utils.ResponseUtils; import org.wfc.omada.api.client.OmadaClientApi; +import org.wfc.omada.api.client.model.ClientInfo; import org.wfc.omada.api.client.model.ClientRateLimitSetting; import org.wfc.omada.api.client.model.CustomRateLimitEntity; +import org.wfc.omada.api.client.model.OperationResponseClientGridVoClientInfo; import org.wfc.omada.api.client.model.OperationResponseWithoutResult; import org.wfc.omada.api.hotspot.OmadaAuthorizedClientApi; import org.wfc.omada.api.sitesetting.OmadaProfilesApi; @@ -45,15 +48,30 @@ public class OmadaWifiApi extends AbstractOmadaWifiApi { @Override public boolean authClient(String siteId, String clientMac) { - ResponseEntity response = omadaAuthorizedClientApi.authClient(siteId, clientMac); - if (ObjectUtil.isNotNull(response.getBody())) { - if (response.getBody().getErrorCode() == WifiConstants.ERROR_CODE_SUCCESS) { - log.info("authClient success,siteId={},clientMac={},errorCode={}, msg={}", siteId, clientMac, response.getBody().getErrorCode(), response.getBody().getMsg()); - } else { - log.error("authClient error,siteId={},clientMac={},errorCode={}, msg={}", siteId, clientMac, response.getBody().getErrorCode(), response.getBody().getMsg()); + ResponseEntity activeClientsResponse = omadaClientApi.getGridActiveClients(siteId, 1, 1000, clientMac); + if (ObjectUtil.isNotNull(activeClientsResponse.getBody())) { + if (!ResponseUtils.isSuccessResponse(activeClientsResponse.getBody().getErrorCode(), activeClientsResponse.getBody().getMsg())) { + return false; + } + // 查client是否未授权 + Optional unAuthClientOptional = activeClientsResponse.getBody().getResult().getData().stream().filter(c -> c.getMac().equals(clientMac) && c.getAuthStatus() != WifiConstants.AUTH_STATUS_AUTHORIZED).findFirst(); + if (!unAuthClientOptional.isPresent()) { + return false; + } + ResponseEntity response = omadaAuthorizedClientApi.authClient(siteId, clientMac); + if (ObjectUtil.isNotNull(response.getBody())) { + if (response.getBody().getErrorCode() == WifiConstants.ERROR_CODE_SUCCESS) { + log.info("authClient success,siteId={},clientMac={},errorCode={}, msg={}", siteId, clientMac, response.getBody().getErrorCode(), response.getBody().getMsg()); + // 授权成功后reconnect, 切割出pending的流量 + reconnectClient(siteId, clientMac); + return true; + } else { + log.error("authClient error,siteId={},clientMac={},errorCode={}, msg={}", siteId, clientMac, response.getBody().getErrorCode(), response.getBody().getMsg()); + return false; + } } } - return true; + return false; } @Override diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UCdrHistory.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UCdrHistory.java index 126ffdb..0c40aed 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UCdrHistory.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UCdrHistory.java @@ -45,4 +45,10 @@ public class UCdrHistory extends BaseData { @Schema(description = "Duration(s)") private Long duration; + + @Schema(description = "useType") + private Integer useType; + + @Schema(description = "useId") + private Long useId; } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UClient.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UClient.java index 19eb5b7..4d8ff63 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UClient.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UClient.java @@ -42,4 +42,13 @@ public class UClient extends BaseData { @Schema(description = "Client mac address") private String clientMac; + + @Schema(description = "active") + private Boolean active; + + @Schema(description = "useType") + private Integer useType; + + @Schema(description = "useId") + private Long useId; } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/constant/UserTypeEnum.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/constant/UserTypeEnum.java new file mode 100644 index 0000000..770577b --- /dev/null +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/constant/UserTypeEnum.java @@ -0,0 +1,21 @@ +package org.wfc.user.domain.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @description: 使用类型枚举 + * @author: cyc + * @since: 2024-2-26 + */ +@Getter +@AllArgsConstructor +public enum UserTypeEnum { + + PACKAGE(0, "套餐"), + BALANCE(1, "余额"), + OTHER(2, "其他"); + + private final Integer code; + private final String desc; +} diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UAccountDashboardVo.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UAccountDashboardVo.java index 95aad78..071f105 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UAccountDashboardVo.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UAccountDashboardVo.java @@ -65,4 +65,6 @@ public class UAccountDashboardVo { private Boolean upLimitEnable; private Long activity; + + private Integer status; } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/mapper/UCdrMapper.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/mapper/UCdrMapper.java index fc4816c..244b4a5 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/mapper/UCdrMapper.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/mapper/UCdrMapper.java @@ -3,6 +3,7 @@ package org.wfc.user.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; import org.wfc.user.domain.UCdr; +import org.wfc.user.domain.UClient; import org.wfc.user.domain.bo.UCdrClientBo; import org.wfc.user.domain.vo.UCdrClientVo; import org.wfc.user.domain.vo.UCdrHistoryUserVo; @@ -31,4 +32,6 @@ public interface UCdrMapper extends BaseMapper { List getLatestHistory(@Param("userIds") List userIds, @Param("endTime") Date endTime); Long getCurrentTraffic(@Param("userId") Long userId); - } + + UClient getClientByFirstSeen(@Param("siteId") String siteId, @Param("clientMac") String clientMac, @Param("firstSeen") Long firstSeen); +} diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUAccountService.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUAccountService.java index c9f55bf..55ee054 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUAccountService.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUAccountService.java @@ -32,4 +32,6 @@ public interface IUAccountService extends IService { */ UAccountDashboardVo getByUser(); + void generateBill(Long userId, Long cdrHistoryId); + } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UAccountServiceImpl.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UAccountServiceImpl.java index e4dcbe1..852b02f 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UAccountServiceImpl.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UAccountServiceImpl.java @@ -1,8 +1,6 @@ package org.wfc.user.service.impl; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.DateTime; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -31,8 +29,9 @@ import org.wfc.user.domain.UAccount; import org.wfc.user.domain.UBill; import org.wfc.user.domain.UBillRule; import org.wfc.user.domain.UClient; +import org.wfc.user.domain.constant.OrderStatusEnum; +import org.wfc.user.domain.constant.OrderTypeEnum; import org.wfc.user.domain.vo.UAccountDashboardVo; -import org.wfc.user.domain.vo.UCdrLatestHistoryVo; import org.wfc.user.domain.vo.UCdrUserVo; import org.wfc.user.domain.vo.UClientCurrentVo; import org.wfc.user.mapper.UAccountMapper; @@ -46,7 +45,6 @@ import org.wfc.user.util.AccountUtil; import org.wfc.user.util.BillRuleUtil; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.List; @@ -90,18 +88,14 @@ public class UAccountServiceImpl extends ServiceImpl i @Lazy private IUClientService uClientService; - - // 定时任务时间间隔 - private static final int JOB_PERIOD = -30; - @Transactional(rollbackFor = Exception.class) public void statAndCancelAuthUser() { // 定时任务查询所有未过期或刚过期(失效时间+定时任务间隔时间)的账户套餐,套餐过期/流量用完/时长用完则取消授权 Date current = new Date(); - DateTime endTime = DateUtil.offsetSecond(current, JOB_PERIOD); + // 查出 List accounts = this.list(Wrappers.lambdaQuery() .and(wrapper -> wrapper.isNotNull(UAccount::getBalance).gt(UAccount::getBalance, 0).or() - .le(UAccount::getStartTime, endTime).gt(UAccount::getEndTime, endTime)) + .le(UAccount::getStartTime, current).gt(UAccount::getEndTime, current)) .isNotNull(UAccount::getUserId)); List packageAccounts = accounts.stream().filter(account -> AccountUtil.isPackageValid(account, current)) @@ -110,77 +104,25 @@ public class UAccountServiceImpl extends ServiceImpl i List balanceAccounts = accounts.stream().filter(account -> !AccountUtil.isPackageValid(account, current)) .collect(Collectors.toList()); - // 根据使用流量扣费余额 - List balanceUserIds = balanceAccounts.stream().map(UAccount::getUserId).collect(Collectors.toList()); - - List cancelUserIds = new ArrayList<>(); - if (CollUtil.isNotEmpty(balanceUserIds)) { - List latestHistoryList = cdrMapper.getLatestHistory(balanceUserIds, endTime); + // 统计已使用余额 + if (CollUtil.isNotEmpty(balanceAccounts)) { List billRules = billRuleMapper.selectList(Wrappers.lambdaQuery().eq(UBillRule::getEnable, true)); Optional billRuleOptional = billRules.stream().findFirst(); if (billRuleOptional.isPresent()) { UBillRule billRule = billRuleOptional.get(); - for (Long balanceUserId : balanceUserIds) { - Long currentTraffic = cdrMapper.getCurrentTraffic(balanceUserId); + for (UAccount balanceAccount : balanceAccounts) { + // 根据使用流量扣费余额 + Long currentTraffic = cdrMapper.getCurrentTraffic(balanceAccount.getUserId()); if (ObjectUtil.isNull(currentTraffic)) { continue; } - UAccount account = this.getOne(Wrappers.lambdaQuery().eq(UAccount::getUserId, balanceUserId), false); - account.setBalanceUsed(BillRuleUtil.calc(billRule, currentTraffic)); - if (ObjectUtil.isNull(account.getBalance())) { - account.setBalance(BigDecimal.ZERO); - } - if (account.getBalance().compareTo(account.getBalanceUsed()) <= 0) { - if (account.getBalanceUsed().compareTo(BigDecimal.ZERO) > 0) { - UBill bill = new UBill(); - bill.setUserId(account.getUserId()); - bill.setType(2); - bill.setStatus(1); - bill.setAmount(account.getBalanceUsed()); - billMapper.insert(bill); - } - account.setBalanceUsed(BigDecimal.ZERO); - account.setBalance(BigDecimal.ZERO); - cancelUserIds.add(account.getUserId()); - } - this.updateById(account); - } - - for (UCdrLatestHistoryVo historyVo : latestHistoryList) { - List uBills = billMapper.selectList(Wrappers.lambdaQuery().eq(UBill::getCdrHistoryId, historyVo.getId())); - if (CollUtil.isNotEmpty(uBills)) { - continue; - } - UBill bill = new UBill(); - bill.setUserId(historyVo.getUserId()); - bill.setCdrHistoryId(historyVo.getId()); - bill.setStatus(1); - bill.setType(2); - bill.setAmount(BillRuleUtil.calc(billRule, historyVo.getTrafficDown() + historyVo.getTrafficUp())); - billMapper.insert(bill); - - UAccount account = this.getOne(Wrappers.lambdaQuery().eq(UAccount::getUserId, historyVo.getUserId()), false); - if (ObjectUtil.isNull(account) || (ObjectUtil.isNotNull(account.getExpiredTime()) - && account.getExpiredTime().getTime() >= historyVo.getEndTime())) { - continue; - } - if (ObjectUtil.isNull(account.getBalance())) { - account.setBalance(BigDecimal.ZERO); - } - if (account.getBalance().compareTo(bill.getAmount()) > 0) { - account.setBalance(account.getBalance().subtract(bill.getAmount())); - account.setBalanceUsed(BigDecimal.ZERO); - } else { - account.setBalance(BigDecimal.ZERO); - account.setBalanceUsed(BigDecimal.ZERO); - cancelUserIds.add(account.getUserId()); - } - this.updateById(account); + balanceAccount.setBalanceUsed(BillRuleUtil.calc(billRule, currentTraffic)); } + this.updateBatchById(balanceAccounts); } } - // 更新账户已使用流量,已使用时长 + // 统计并更新账户已使用流量,已使用时长 for (UAccount account : packageAccounts) { if (ObjectUtil.isNull(account.getUserId())) { continue; @@ -193,59 +135,8 @@ public class UAccountServiceImpl extends ServiceImpl i } this.updateBatchById(packageAccounts); - List changeUserIds = packageAccounts.stream().filter(account -> !AccountUtil.isPackageValid(account, current) && AccountUtil.isBalanceValid(account)) - .map(UAccount::getUserId) - .collect(Collectors.toList()); - if (CollUtil.isNotEmpty(changeUserIds)) { - List clients = clientMapper.selectList(Wrappers.lambdaQuery().isNotNull(UClient::getSiteId) - .in(UClient::getUserId, changeUserIds)); - for (UClient client : clients) { - try { - wifiApi.reconnectClient(client.getSiteId(), client.getClientMac()); - - // 取消带宽限速 - ClientRateLimitSettingDto clientRateLimitSetting = new ClientRateLimitSettingDto(); - clientRateLimitSetting.setRateLimitEnable(false); - wifiApi.updateClientRateLimitSetting(client.getSiteId(), client.getClientMac(), clientRateLimitSetting); - } catch (Exception e) { - log.info("reconnect error: {}", e.getMessage()); - } - } - ArrayList changeAccounts = new ArrayList<>(); - Date expiredTime = new Date(); - for (Long changeUserId : changeUserIds) { - UAccount changeAccount = new UAccount(); - changeAccount.setId(changeUserId); - changeAccount.setExpiredTime(expiredTime); - } - this.updateBatchById(changeAccounts); - } - // 取消授权 - List userIds = packageAccounts.stream().filter(account -> !AccountUtil.isValid(account, current)) - .map(UAccount::getUserId) - .collect(Collectors.toList()); - userIds.addAll(cancelUserIds); - - if (CollUtil.isNotEmpty(userIds)) { - List clients = clientMapper.selectList(Wrappers.lambdaQuery().isNotNull(UClient::getSiteId) - .in(UClient::getUserId, userIds)); - for (UClient client : clients) { - try { - wifiApi.cancelAuthClient(client.getSiteId(), client.getClientMac()); - - // 取消带宽限速 - ClientRateLimitSettingDto clientRateLimitSetting = new ClientRateLimitSettingDto(); - clientRateLimitSetting.setRateLimitEnable(false); - wifiApi.updateClientRateLimitSetting(client.getSiteId(), client.getClientMac(), clientRateLimitSetting); - } catch (Exception e) { - log.info("unAuth error: {}", e.getMessage()); - } - } - } - cancelAuthClient(); - } private void cancelAuthClient() { @@ -272,6 +163,15 @@ public class UAccountServiceImpl extends ServiceImpl i continue; } UAccount account = this.getOne(Wrappers.lambdaQuery().eq(UAccount::getUserId, loginClient.getUserId()), false); + // 套餐失效,但是有余额且没有已使用余额,reconnect + if (AccountUtil.isPackageUnValidBalanceValid(account, current)) { + wifiApi.reconnectClient(site.getSiteId(), client.getMac()); + } + // 套餐有效,有已使用余额且余额扣去已使用余额大于0,reconnect + if (AccountUtil.isPackageValidBalanceUnValid(account, current)) { + wifiApi.reconnectClient(site.getSiteId(), client.getMac()); + } + if (ObjectUtil.isNull(account)) { wifiApi.cancelAuthClient(site.getSiteId(), client.getMac()); continue; @@ -336,6 +236,7 @@ public class UAccountServiceImpl extends ServiceImpl i dashboardVo.setClientNum(0); dashboardVo.setClientNumUsed(0); dashboardVo.setActivity(0L); + dashboardVo.setStatus(0); } else { BeanUtils.copyProperties(account, dashboardVo); dashboardVo.setId(loginUser.getUserid()); @@ -348,8 +249,43 @@ public class UAccountServiceImpl extends ServiceImpl i } else { dashboardVo.setBalance(BigDecimal.ZERO); } + if (AccountUtil.isPackageValid(account, new Date())) { + dashboardVo.setStatus(1); + } else { + dashboardVo.setStatus(0); + } + } return dashboardVo; } + public void generateBill(Long userId, Long cdrHistoryId) { + // 更新当前账户的余额,并且生成一个bill账单 + UAccount account = this.getOne(Wrappers.lambdaQuery().eq(UAccount::getUserId, userId), false); + if (ObjectUtil.isNull(account)) { + return; + } + account.setBalance(Optional.ofNullable(account.getBalance()).orElse(BigDecimal.ZERO)); + account.setBalanceUsed(Optional.ofNullable(account.getBalanceUsed()).orElse(BigDecimal.ZERO)); + if (account.getBalanceUsed().compareTo(BigDecimal.ZERO) == 0) { + return; + } + if (account.getBalance().compareTo(account.getBalanceUsed()) < 0) { + account.setBalance(BigDecimal.ZERO); + } else { + account.setBalance(account.getBalance().subtract(account.getBalanceUsed())); + } + // 保存账单信息 + UBill bill = new UBill(); + bill.setAmount(account.getBalanceUsed()); + bill.setUserId(userId); + bill.setCdrHistoryId(cdrHistoryId); + bill.setType(OrderTypeEnum.RECHARGE.getCode()); + bill.setStatus(OrderStatusEnum.PAID.getCode()); + billMapper.insert(bill); + + account.setBalanceUsed(BigDecimal.ZERO); + this.updateById(account); + } + } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UCdrServiceImpl.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UCdrServiceImpl.java index 7de7b21..dbb85b2 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UCdrServiceImpl.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UCdrServiceImpl.java @@ -1,13 +1,16 @@ package org.wfc.user.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.wfc.common.core.constant.WifiConstants; import org.wfc.common.core.domain.LoginUser; +import org.wfc.common.core.utils.ResponseUtils; import org.wfc.common.security.utils.SecurityUtils; import org.wfc.omada.api.client.OmadaClientApi; import org.wfc.omada.api.client.OmadaClientInsightApi; @@ -28,6 +31,7 @@ import org.wfc.user.domain.UCdrHistory; import org.wfc.user.domain.UClient; import org.wfc.user.domain.UDevice; import org.wfc.user.domain.bo.UCdrClientBo; +import org.wfc.user.domain.constant.UserTypeEnum; import org.wfc.user.domain.vo.UCdrClientVo; import org.wfc.user.domain.vo.UCdrHistoryUserVo; import org.wfc.user.domain.vo.UCdrUserVo; @@ -39,6 +43,7 @@ import org.wfc.user.service.IUClientService; import org.wfc.user.service.IUDeviceService; import org.wfc.user.util.AccountUtil; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; @@ -110,6 +115,9 @@ public class UCdrServiceImpl extends ServiceImpl implements IU if (ObjectUtil.isNull(siteResp.getBody())) { return; } + if (!ResponseUtils.isSuccessResponse(siteResp.getBody().getErrorCode(), siteResp.getBody().getMsg())) { + return; + } List sites = siteResp.getBody().getResult().getData(); List allClients = clientService.list(Wrappers.lambdaQuery().isNotNull(UClient::getUserId)); // 添加AP设备 @@ -120,36 +128,56 @@ public class UCdrServiceImpl extends ServiceImpl implements IU continue; } List clients = clientResp.getBody().getResult().getData(); - authClients(clients); + // 授权client + List macs = authClients(clients); - for (ClientInfo client : clients) { - // 添加用户设备 - UClient hasClient = addClient(client); - // 添加话单 - addCdr(client, hasClient); - } + // 新增cdr历史 for (UClient client : allClients) { UCdr uCdr = this.getOne(Wrappers.lambdaQuery().eq(UCdr::getUserId, client.getUserId()) .eq(UCdr::getClientId, client.getId()), false); if (ObjectUtil.isNotNull(uCdr)) { - addCdrHistory(site, client.getClientMac(), uCdr.getId()); + addCdrHistory(site.getSiteId(), client.getClientMac(), uCdr.getId()); } } - } + // 根据返回授权成功的clients, 加上当前状态是已授权的clients + clients = clients.stream().filter(c -> WifiConstants.AUTH_STATUS_AUTHORIZED == c.getAuthStatus() || macs.contains(c.getMac())) + .collect(Collectors.toList()); + List activeIds = new ArrayList<>(); + for (ClientInfo client : clients) { + // 添加用户设备 + UClient hasClient = addClient(client, site.getSiteId()); + // 添加话单 + addCdr(client, hasClient); + // 记录使用类型(当前生效套餐或余额) + recordUseType(hasClient); + activeIds.add(hasClient.getId()); + } + // 根据返回已active的client ids, 更新非这些ids的active为false + List unActiveClients = allClients.stream().filter(c -> site.getSiteId().equals(c.getSiteId()) + && !activeIds.contains(c.getId())).map(c -> { + UClient client = new UClient(); + client.setId(c.getId()); + client.setActive(false); + return client; + }).collect(Collectors.toList()); + clientService.updateBatchById(unActiveClients); + } + // 统计和取消授权 accountService.statAndCancelAuthUser(); } - private void authClients(List clients) { + private List authClients(List clients) { + List macs = new ArrayList<>(); List clientMacs = clients.stream().filter(c -> ObjectUtil.isNotNull(c.getAuthStatus()) && c.getAuthStatus() != 2) .map(ClientInfo::getMac).collect(Collectors.toList()); if (CollUtil.isEmpty(clientMacs)) { - return; + return macs; } List unAuthClients = clientService.list(Wrappers.lambdaQuery().isNotNull(UClient::getUserId).in(UClient::getClientMac, clientMacs)); List userIds = unAuthClients.stream().map(UClient::getUserId).collect(Collectors.toList()); if (CollUtil.isEmpty(userIds)) { - return; + return macs; } List accounts = accountService.list(Wrappers.lambdaQuery().in(UAccount::getUserId, userIds)); for (UClient unAuthClient : unAuthClients) { @@ -162,17 +190,23 @@ public class UCdrServiceImpl extends ServiceImpl implements IU continue; } } - wifiApi.authClient(unAuthClient.getSiteId(), unAuthClient.getClientMac()); + // 授权成功返回,以及考虑是否reconnect,截出pending部分 + boolean authFlag = wifiApi.authClient(unAuthClient.getSiteId(), unAuthClient.getClientMac()); + if (!authFlag) { + macs.add(unAuthClient.getClientMac()); + } } } } } + return macs; } - private void addCdrHistory(SiteSummaryInfo site, String mac, Long cdrId) { + private void addCdrHistory(String siteId, String mac, Long cdrId) { // 话单历史 - ResponseEntity pastConnResp = omadaClientInsightApi.getGridPastConnections(site.getSiteId(), 1, 1000, mac); + // 时间戳范围,取当前时间一天前到现在的范围 + ResponseEntity pastConnResp = omadaClientInsightApi.getGridPastConnections(siteId, 1, 1000, "asc",DateUtil.yesterday().getTime() + "", System.currentTimeMillis() + "", mac); if (ObjectUtil.isNull(pastConnResp.getBody())) { return; } @@ -182,6 +216,7 @@ public class UCdrServiceImpl extends ServiceImpl implements IU if (ObjectUtil.isNotNull(hasCdrHistory)) { continue; } + UCdrHistory uCdrHistory = UCdrHistory.builder().cdrId(cdrId).startTime(pastConn.getFirstSeen()) .endTime(pastConn.getLastSeen()) .trafficUp(pastConn.getUpload()) @@ -189,7 +224,23 @@ public class UCdrServiceImpl extends ServiceImpl implements IU .duration(pastConn.getDuration()) .build(); + // 将在线的client时查出的使用类型(套餐或余额)记录到历史数据中 + UClient client = this.baseMapper.getClientByFirstSeen(siteId, mac, pastConn.getFirstSeen()); + if (client != null) { + uCdrHistory.setUseType(client.getUseType()); + if (UserTypeEnum.PACKAGE.getCode().equals(client.getUseType())) { + uCdrHistory.setUseId(client.getUseId()); + } + } + cdrHistoryService.save(uCdrHistory); + + if (client != null) { + // 如果是余额类型,更新当前账户的余额,并且生成一个bill账单 + if (UserTypeEnum.BALANCE.getCode().equals(client.getUseType())) { + accountService.generateBill(client.getUserId(), uCdrHistory.getId()); + } + } } } @@ -222,9 +273,10 @@ public class UCdrServiceImpl extends ServiceImpl implements IU return uCdr.getId(); } - private UClient addClient(ClientInfo client) { + private UClient addClient(ClientInfo client, String siteId) { // 用户设备 - UClient hasClient = clientService.getOne(Wrappers.lambdaQuery().eq(UClient::getClientMac, client.getMac()), false); + UClient hasClient = clientService.getOne(Wrappers.lambdaQuery().eq(UClient::getSiteId, siteId) + .eq(UClient::getClientMac, client.getMac()), false); Long clientId = null; Long userId = null; if (ObjectUtil.isNotNull(hasClient)) { @@ -237,10 +289,29 @@ public class UCdrServiceImpl extends ServiceImpl implements IU .clientDeviceType(client.getDeviceType()) .build(); uClient.setId(clientId); + clientService.saveOrUpdate(uClient); return uClient; } + private void recordUseType(UClient uClient) { + // 更新active为true,查当前use是否有account,account是否有套餐或余额,记录当前使用的是套餐还是余额 + uClient.setActive(true); + Long userId = uClient.getUserId(); + if (userId != null) { + UAccount account = accountService.getOne(Wrappers.lambdaQuery().eq(UAccount::getUserId, userId), false); + if (AccountUtil.isPackageValidNoUsedBalance(account, new Date())) { + uClient.setUseType(UserTypeEnum.PACKAGE.getCode()); + uClient.setUseId(account.getPackageId()); + } else if (AccountUtil.isBalanceValid(account)) { + uClient.setUseType(UserTypeEnum.BALANCE.getCode()); + } else { + uClient.setUseType(UserTypeEnum.OTHER.getCode()); + } + } + clientService.updateById(uClient); + } + private void addDevices(List sites) { // ap设备 for (SiteSummaryInfo site : sites) { diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UClientServiceImpl.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UClientServiceImpl.java index 2e8846f..658f8d3 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UClientServiceImpl.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UClientServiceImpl.java @@ -49,10 +49,11 @@ public class UClientServiceImpl extends ServiceImpl impl @Override public boolean recordClientUser(UClientBo uClientBo) { - if (StrUtil.isBlank(uClientBo.getClientMac())) { + if (StrUtil.isBlank(uClientBo.getClientMac()) || StrUtil.isBlank(uClientBo.getSiteId())) { return false; } UClient hasUClient = this.getOne(Wrappers.lambdaQuery() + .eq(UClient::getSiteId, uClientBo.getSiteId()) .eq(UClient::getClientMac, uClientBo.getClientMac()), false); UClient uClient = new UClient(); BeanUtils.copyProperties(uClientBo, uClient); diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java index 26f43e5..db48d1b 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java @@ -120,6 +120,12 @@ public class UOrderServiceImpl extends ServiceImpl impleme // 充值 // 更新账户余额 account.setBalance(order.getOrderAmount().add(Optional.ofNullable(account.getBalance()).orElse(BigDecimal.ZERO))); + // 如果已欠费,充值时补充欠费部分以抵消 + if (ObjectUtil.isNull(account.getBalanceUsed()) && account.getBalanceUsed().compareTo(BigDecimal.ZERO) > 0 + && ObjectUtil.isNull(account.getBalance()) && account.getBalance().subtract(account.getBalanceUsed()).compareTo(BigDecimal.ZERO) < 0) { + account.setBalance(account.getBalance().add(account.getBalanceUsed().subtract(account.getBalance()))); + } + } account.setId(accountId); diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/util/AccountUtil.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/util/AccountUtil.java index f1efaa0..0d85731 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/util/AccountUtil.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/util/AccountUtil.java @@ -26,18 +26,47 @@ public class AccountUtil { return ((DateUtil.compare(account.getStartTime(), current) <= 0 && DateUtil.compare(account.getEndTime(), current) > 0) && (!account.getTrafficEnable() || account.getTrafficUsed() <= account.getTraffic()) && (!account.getDurationEnable() || account.getDurationUsed() <= account.getDuration())) - || (ObjectUtil.isNotNull(account.getBalance()) && account.getBalance().compareTo(BigDecimal.ZERO) > 0); + || (ObjectUtil.isNotNull(account.getBalance()) && account.getBalance().compareTo(BigDecimal.ZERO) > 0 + && (ObjectUtil.isNull(account.getBalanceUsed()) || account.getBalanceUsed().compareTo(BigDecimal.ZERO) == 0 + || (ObjectUtil.isNotNull(account.getBalanceUsed()) && account.getBalance().compareTo(BigDecimal.ZERO) > 0 + && account.getBalance().subtract(account.getBalanceUsed()).compareTo(BigDecimal.ZERO) > 0))); } public static boolean isPackageValid(UAccount account, Date current) { - // 过期/流量已用完/时长已用完应取消授权 + // 套餐是否有效 return (DateUtil.compare(account.getStartTime(), current) <= 0 && DateUtil.compare(account.getEndTime(), current) > 0) && (!account.getTrafficEnable() || account.getTrafficUsed() <= account.getTraffic()) && (!account.getDurationEnable() || account.getDurationUsed() <= account.getDuration()); } public static boolean isBalanceValid(UAccount account) { - // 过期/流量已用完/时长已用完应取消授权 + // 余额是否有效 return ObjectUtil.isNotNull(account.getBalance()) && account.getBalance().compareTo(BigDecimal.ZERO) > 0; } + + public static boolean isPackageUnValidBalanceValid(UAccount account, Date current) { + // 套餐失效,但是有余额且没有已使用余额 + return !((DateUtil.compare(account.getStartTime(), current) <= 0 && DateUtil.compare(account.getEndTime(), current) > 0) + && (!account.getTrafficEnable() || account.getTrafficUsed() <= account.getTraffic()) + && (!account.getDurationEnable() || account.getDurationUsed() <= account.getDuration())) + && (ObjectUtil.isNotNull(account.getBalance()) && account.getBalance().compareTo(BigDecimal.ZERO) > 0) + && (ObjectUtil.isNull(account.getBalanceUsed()) || account.getBalanceUsed().compareTo(BigDecimal.ZERO) == 0); + } + + public static boolean isPackageValidBalanceUnValid(UAccount account, Date current) { + // 套餐有效,有已使用余额且余额扣去已使用余额大于0 + return ((DateUtil.compare(account.getStartTime(), current) <= 0 && DateUtil.compare(account.getEndTime(), current) > 0) + && (!account.getTrafficEnable() || account.getTrafficUsed() <= account.getTraffic()) + && (!account.getDurationEnable() || account.getDurationUsed() <= account.getDuration())) + && (ObjectUtil.isNotNull(account.getBalanceUsed()) && account.getBalanceUsed().compareTo(BigDecimal.ZERO) > 0) + && (ObjectUtil.isNotNull(account.getBalance()) && account.getBalance().subtract(account.getBalanceUsed()).compareTo(BigDecimal.ZERO) > 0); + } + + public static boolean isPackageValidNoUsedBalance(UAccount account, Date current) { + // 套餐有效,没有已使用余额 + return ((DateUtil.compare(account.getStartTime(), current) <= 0 && DateUtil.compare(account.getEndTime(), current) > 0) + && (!account.getTrafficEnable() || account.getTrafficUsed() <= account.getTraffic()) + && (!account.getDurationEnable() || account.getDurationUsed() <= account.getDuration())) + && (ObjectUtil.isNull(account.getBalanceUsed()) || account.getBalanceUsed().compareTo(BigDecimal.ZERO) == 0); + } } diff --git a/wfc-modules/wfc-user/src/main/resources/mapper/user/UCdrMapper.xml b/wfc-modules/wfc-user/src/main/resources/mapper/user/UCdrMapper.xml index 942c55d..e32e9af 100644 --- a/wfc-modules/wfc-user/src/main/resources/mapper/user/UCdrMapper.xml +++ b/wfc-modules/wfc-user/src/main/resources/mapper/user/UCdrMapper.xml @@ -26,7 +26,7 @@ AND ch.del_flag = 0 WHERE cdr.del_flag = 0 - AND cdr.last_seen_time != ifnull( ch.end_time, 0 ) + AND ch.use_type = 0 AND cdr.user_id = #{userId} @@ -49,14 +49,13 @@ ifnull( cdr.traffic_up, 0 )) traffic_up FROM u_cdr cdr + LEFT JOIN u_client c ON cdr.client_id = c.id AND c.del_flag = 0 WHERE cdr.del_flag = 0 AND cdr.user_id = #{userId} - - AND cdr.last_seen_time <= #{endTime} - + AND c.active = 1 GROUP BY cdr.user_id ) t @@ -157,17 +156,34 @@ + +