2
0

fix: 调整余额扣费功能

This commit is contained in:
caiyuchao
2025-02-27 17:14:04 +08:00
parent 03aae62a4c
commit 3cbae167dc
17 changed files with 310 additions and 164 deletions

View File

@@ -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<org.wfc.omada.api.hotspot.model.OperationResponseWithoutResult> 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<OperationResponseClientGridVoClientInfo> 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<ClientInfo> 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<org.wfc.omada.api.hotspot.model.OperationResponseWithoutResult> 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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -65,4 +65,6 @@ public class UAccountDashboardVo {
private Boolean upLimitEnable;
private Long activity;
private Integer status;
}

View File

@@ -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<UCdr> {
List<UCdrLatestHistoryVo> getLatestHistory(@Param("userIds") List<Long> 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);
}

View File

@@ -32,4 +32,6 @@ public interface IUAccountService extends IService<UAccount> {
*/
UAccountDashboardVo getByUser();
void generateBill(Long userId, Long cdrHistoryId);
}

View File

@@ -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<UAccountMapper, UAccount> 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<UAccount> accounts = this.list(Wrappers.<UAccount>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<UAccount> packageAccounts = accounts.stream().filter(account -> AccountUtil.isPackageValid(account, current))
@@ -110,77 +104,25 @@ public class UAccountServiceImpl extends ServiceImpl<UAccountMapper, UAccount> i
List<UAccount> balanceAccounts = accounts.stream().filter(account -> !AccountUtil.isPackageValid(account, current))
.collect(Collectors.toList());
// 根据使用流量扣费余额
List<Long> balanceUserIds = balanceAccounts.stream().map(UAccount::getUserId).collect(Collectors.toList());
List<Long> cancelUserIds = new ArrayList<>();
if (CollUtil.isNotEmpty(balanceUserIds)) {
List<UCdrLatestHistoryVo> latestHistoryList = cdrMapper.getLatestHistory(balanceUserIds, endTime);
// 统计已使用余额
if (CollUtil.isNotEmpty(balanceAccounts)) {
List<UBillRule> billRules = billRuleMapper.selectList(Wrappers.<UBillRule>lambdaQuery().eq(UBillRule::getEnable, true));
Optional<UBillRule> 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.<UAccount>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<UBill> uBills = billMapper.selectList(Wrappers.<UBill>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.<UAccount>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<UAccountMapper, UAccount> i
}
this.updateBatchById(packageAccounts);
List<Long> changeUserIds = packageAccounts.stream().filter(account -> !AccountUtil.isPackageValid(account, current) && AccountUtil.isBalanceValid(account))
.map(UAccount::getUserId)
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(changeUserIds)) {
List<UClient> clients = clientMapper.selectList(Wrappers.<UClient>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<UAccount> 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<Long> userIds = packageAccounts.stream().filter(account -> !AccountUtil.isValid(account, current))
.map(UAccount::getUserId)
.collect(Collectors.toList());
userIds.addAll(cancelUserIds);
if (CollUtil.isNotEmpty(userIds)) {
List<UClient> clients = clientMapper.selectList(Wrappers.<UClient>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<UAccountMapper, UAccount> i
continue;
}
UAccount account = this.getOne(Wrappers.<UAccount>lambdaQuery().eq(UAccount::getUserId, loginClient.getUserId()), false);
// 套餐失效但是有余额且没有已使用余额reconnect
if (AccountUtil.isPackageUnValidBalanceValid(account, current)) {
wifiApi.reconnectClient(site.getSiteId(), client.getMac());
}
// 套餐有效有已使用余额且余额扣去已使用余额大于0reconnect
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<UAccountMapper, UAccount> 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<UAccountMapper, UAccount> 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.<UAccount>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);
}
}

View File

@@ -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<UCdrMapper, UCdr> implements IU
if (ObjectUtil.isNull(siteResp.getBody())) {
return;
}
if (!ResponseUtils.isSuccessResponse(siteResp.getBody().getErrorCode(), siteResp.getBody().getMsg())) {
return;
}
List<SiteSummaryInfo> sites = siteResp.getBody().getResult().getData();
List<UClient> allClients = clientService.list(Wrappers.<UClient>lambdaQuery().isNotNull(UClient::getUserId));
// 添加AP设备
@@ -120,36 +128,56 @@ public class UCdrServiceImpl extends ServiceImpl<UCdrMapper, UCdr> implements IU
continue;
}
List<ClientInfo> clients = clientResp.getBody().getResult().getData();
authClients(clients);
// 授权client
List<String> macs = authClients(clients);
for (ClientInfo client : clients) {
// 添加用户设备
UClient hasClient = addClient(client);
// 添加话单
addCdr(client, hasClient);
}
// 新增cdr历史
for (UClient client : allClients) {
UCdr uCdr = this.getOne(Wrappers.<UCdr>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<Long> 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<UClient> 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<ClientInfo> clients) {
private List<String> authClients(List<ClientInfo> clients) {
List<String> macs = new ArrayList<>();
List<String> 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<UClient> unAuthClients = clientService.list(Wrappers.<UClient>lambdaQuery().isNotNull(UClient::getUserId).in(UClient::getClientMac, clientMacs));
List<Long> userIds = unAuthClients.stream().map(UClient::getUserId).collect(Collectors.toList());
if (CollUtil.isEmpty(userIds)) {
return;
return macs;
}
List<UAccount> accounts = accountService.list(Wrappers.<UAccount>lambdaQuery().in(UAccount::getUserId, userIds));
for (UClient unAuthClient : unAuthClients) {
@@ -162,17 +190,23 @@ public class UCdrServiceImpl extends ServiceImpl<UCdrMapper, UCdr> 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<OperationResponseGridVoClientHistoryInfo> pastConnResp = omadaClientInsightApi.getGridPastConnections(site.getSiteId(), 1, 1000, mac);
// 时间戳范围,取当前时间一天前到现在的范围
ResponseEntity<OperationResponseGridVoClientHistoryInfo> 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<UCdrMapper, UCdr> 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<UCdrMapper, UCdr> 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<UCdrMapper, UCdr> implements IU
return uCdr.getId();
}
private UClient addClient(ClientInfo client) {
private UClient addClient(ClientInfo client, String siteId) {
// 用户设备
UClient hasClient = clientService.getOne(Wrappers.<UClient>lambdaQuery().eq(UClient::getClientMac, client.getMac()), false);
UClient hasClient = clientService.getOne(Wrappers.<UClient>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<UCdrMapper, UCdr> implements IU
.clientDeviceType(client.getDeviceType())
.build();
uClient.setId(clientId);
clientService.saveOrUpdate(uClient);
return uClient;
}
private void recordUseType(UClient uClient) {
// 更新active为true查当前use是否有accountaccount是否有套餐或余额记录当前使用的是套餐还是余额
uClient.setActive(true);
Long userId = uClient.getUserId();
if (userId != null) {
UAccount account = accountService.getOne(Wrappers.<UAccount>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<SiteSummaryInfo> sites) {
// ap设备
for (SiteSummaryInfo site : sites) {

View File

@@ -49,10 +49,11 @@ public class UClientServiceImpl extends ServiceImpl<UClientMapper, UClient> 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.<UClient>lambdaQuery()
.eq(UClient::getSiteId, uClientBo.getSiteId())
.eq(UClient::getClientMac, uClientBo.getClientMac()), false);
UClient uClient = new UClient();
BeanUtils.copyProperties(uClientBo, uClient);

View File

@@ -120,6 +120,12 @@ public class UOrderServiceImpl extends ServiceImpl<UOrderMapper, UOrder> 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);

View File

@@ -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);
}
}

View File

@@ -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
<if test="userId != null and userId != ''">
AND cdr.user_id = #{userId}
</if>
@@ -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
<if test="userId != null and userId != ''">
AND cdr.user_id = #{userId}
</if>
<if test="endTime != null">
AND cdr.last_seen_time &lt;= #{endTime}
</if>
AND c.active = 1
GROUP BY
cdr.user_id
) t
@@ -157,17 +156,34 @@
<select id="getCurrentTraffic" resultType="long">
SELECT
sum(
ifnull( c.traffic_down, 0 )) + sum(
ifnull( c.traffic_up, 0 )) traffic
sum(ifnull( cdr.traffic_down, 0 )) +
sum(ifnull( cdr.traffic_up, 0 )) traffic
FROM
u_cdr c
LEFT JOIN ( SELECT h.cdr_id, max( h.end_time ) end_time FROM u_cdr_history h WHERE h.del_flag = 0 GROUP BY h.cdr_id ) h ON c.id = h.cdr_id
u_client c
LEFT JOIN u_cdr cdr ON c.id = cdr.client_id
AND cdr.del_flag = 0
WHERE
c.del_flag = 0
AND c.last_seen_time != ifnull( h.end_time, 0 )
AND c.user_id = #{userId}
c.del_flag = 0
AND c.active = 1
AND c.user_id = #{userId}
AND cdr.id IS NOT NULL
GROUP BY
c.user_id
c.user_id
</select>
<select id="getClientByFirstSeen" resultType="org.wfc.user.domain.UClient">
SELECT
c.*
FROM
u_client c
LEFT JOIN u_cdr cdr ON c.id = cdr.client_id AND cdr.del_flag = 0
WHERE
c.del_flag = 0
AND c.site_id = #{siteId}
AND c.client_mac = #{clientMac}
AND cdr.last_seen_time - cdr.up_time * 10000 &lt; #{firstSeen}
AND cdr.last_seen_time - cdr.up_time * 1000 >= #{firstSeen}
AND cdr.id is not NULL
LIMIT 1
</select>
</mapper>