diff --git a/game-dao/src/main/java/awesome/group/game/dao/bean/MatrixAdvAggregationSub.java b/game-dao/src/main/java/awesome/group/game/dao/bean/MatrixAdvAggregationSub.java new file mode 100644 index 0000000..ae07451 --- /dev/null +++ b/game-dao/src/main/java/awesome/group/game/dao/bean/MatrixAdvAggregationSub.java @@ -0,0 +1,21 @@ +package awesome.group.game.dao.bean; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; + +import java.sql.Timestamp; + +@Data +public class MatrixAdvAggregationSub { + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + private Integer appId; + private Integer adminId; + private Integer date; + private Integer platform;//1穿山甲,2腾讯,3百度联盟,4 Mintegral,5 快手,6游可赢,7 Sigmob,8 Admob + private Long income;//ecpm, 单位:分 + private Long incomeReal;//抽成后ecpm,单位:分 + private Integer incomeRate;//单位:分 + private Timestamp createdAt; +} diff --git a/game-dao/src/main/java/awesome/group/game/dao/mapper/MatrixAdvAggregationSubMapper.java b/game-dao/src/main/java/awesome/group/game/dao/mapper/MatrixAdvAggregationSubMapper.java new file mode 100644 index 0000000..a82161b --- /dev/null +++ b/game-dao/src/main/java/awesome/group/game/dao/mapper/MatrixAdvAggregationSubMapper.java @@ -0,0 +1,17 @@ +package awesome.group.game.dao.mapper; + +import awesome.group.game.dao.bean.MatrixAdvAggregationSub; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Insert; + +import java.util.List; + +public interface MatrixAdvAggregationSubMapper extends BaseMapper { + @Insert("") + int insertBatch(List data); +} diff --git a/game-service/src/main/java/awesome/group/game/service/AdminDeviceService.java b/game-service/src/main/java/awesome/group/game/service/AdminDeviceService.java index b05559a..c03f703 100644 --- a/game-service/src/main/java/awesome/group/game/service/AdminDeviceService.java +++ b/game-service/src/main/java/awesome/group/game/service/AdminDeviceService.java @@ -1,19 +1,29 @@ package awesome.group.game.service; -import awesome.group.game.dao.bean.MatrixAdmin; -import awesome.group.game.dao.bean.MatrixAdminDevice; -import awesome.group.game.dao.bean.MatrixApp; -import awesome.group.game.dao.mapper.MatrixAdminDeviceMapper; -import awesome.group.game.dao.mapper.MatrixAdminMapper; -import awesome.group.game.dao.mapper.MatrixAppMapper; +import awesome.group.game.dao.bean.*; +import awesome.group.game.dao.mapper.*; +import awesome.group.game.service.bo.DateIncome; +import awesome.group.game.service.bo.IncomeQuery; +import awesome.group.game.service.bo.OverviewBo; +import awesome.group.game.service.common.exception.PaganiException; +import awesome.group.game.service.common.exception.PaganiExceptionCode; +import awesome.group.game.service.util.DateUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.util.Arrays; -import java.util.List; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.*; +import java.util.stream.Collectors; @Service public class AdminDeviceService { @@ -29,6 +39,18 @@ public class AdminDeviceService { @Autowired private MatrixAppMapper appMapper; + @Autowired + private MatrixAdvAggregationSubMapper subMapper; + + @Autowired + private AggregationSubService aggregationSubService; + + @Autowired + private MatrixAdvRecordMapper advRecordMapper; + + @Autowired + private AdminService adminService; + public Integer getAdminId(String deviceId) { MatrixAdminDevice device = mapper.selectByDeviceId(deviceId); if (device == null || device.getStatus() == STATUS_OFFLINE) { @@ -37,7 +59,7 @@ public class AdminDeviceService { return device.getAdminId(); } - public List getDeviceList(Integer adminId, String appCode ) { + public List getDeviceList(Integer adminId, String appCode) { MatrixApp app = appMapper.queryByCode(appCode); Assert.isTrue(app != null, "非法请求"); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -52,7 +74,7 @@ public class AdminDeviceService { public String bind(String deviceId, String appCode, String adminName) { MatrixAdminDevice device = mapper.selectByDeviceId(deviceId); - if(device != null){ + if (device != null) { return "设备已绑定"; } MatrixAdmin admin = adminMapper.query(adminName); @@ -63,8 +85,8 @@ public class AdminDeviceService { if (app == null) { return "应用不存在"; } - if(!StringUtils.hasText(admin.getAppIds()) || - !Arrays.stream(admin.getAppIds().split(",")).map(Integer::parseInt).toList().contains(app.getId())){ + if (!StringUtils.hasText(admin.getAppIds()) || + !Arrays.stream(admin.getAppIds().split(",")).map(Integer::parseInt).toList().contains(app.getId())) { return "无权限绑定该应用"; } long cnt = queryBindCnt(admin.getId(), app.getId()); @@ -80,7 +102,7 @@ public class AdminDeviceService { return "绑定成功"; } - public long queryBindCnt(int adminId, int appId){ + public long queryBindCnt(int adminId, int appId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(MatrixAdminDevice::getAdminId, adminId); queryWrapper.eq(MatrixAdminDevice::getAppId, appId); @@ -88,5 +110,142 @@ public class AdminDeviceService { return mapper.selectCount(queryWrapper); } + public OverviewBo incomeOverview(int adminId, String code) { + if (StringUtils.hasText(code)) { + MatrixApp app = appMapper.queryByCode(code); + if (app == null) { + return new OverviewBo(); + } + return incomeOverviewByAppIds(Collections.singletonList(app.getId()), adminId); + } + List appIds = adminService.getAdminAppIds(adminId); + if (CollectionUtils.isEmpty(appIds)) { + return new OverviewBo(); + } + return incomeOverviewByAppIds(appIds, adminId); + } + + private OverviewBo incomeOverviewByAppIds(List appIds, int adminId) { + if (CollectionUtils.isEmpty(appIds)) { + return new OverviewBo(); + } + LambdaQueryWrapper advQuery = new QueryWrapper() + .select("sum(ecpm) as ecpm").lambda(); + advQuery.in(MatrixAdvRecord::getAppId, appIds); + advQuery.gt(MatrixAdvRecord::getCreatedAt, new Timestamp(DateUtil.getDayBeginTimestamp(System.currentTimeMillis()))); + advQuery.eq(MatrixAdvRecord::getAdminId, adminId); + MatrixAdvRecord record = advRecordMapper.selectOne(advQuery); + OverviewBo bo = new OverviewBo(); + bo.todayIncome = record == null ? 0 : record.getEcpm(); + + int today = DateUtil.currentDate(); + int yesterday = DateUtil.datePlus(today, -1); + bo.yesterdayIncome = aggregationSubService.getIncome(adminId, appIds, yesterday, yesterday); + bo.totalIncome = aggregationSubService.getIncome(adminId, appIds, 20240401, yesterday); + + LocalDate now = LocalDate.now(); + + // 获取上个月的年份和月份 + YearMonth lastMonth = YearMonth.from(now).minusMonths(1); + + // 获取上个月的第一天和最后一天 + LocalDate firstDayOfLastMonth = lastMonth.atDay(1); + LocalDate lastDayOfLastMonth = lastMonth.atEndOfMonth(); + bo.lastMonthIncome = aggregationSubService.getIncome(adminId, appIds, DateUtil.date2Number(firstDayOfLastMonth), DateUtil.date2Number(lastDayOfLastMonth)); + + YearMonth thisMonth = YearMonth.from(now); + + // 获取本月的第一天 + LocalDate firstDayOfThisMonth = thisMonth.atDay(1); + bo.thisMonthIncome = bo.todayIncome + aggregationSubService.getIncome(adminId, appIds, DateUtil.date2Number(firstDayOfThisMonth), today); + return bo; + } + + public List incomeDaily(IncomeQuery query, int adminId) { + LambdaQueryWrapper wrapper = new QueryWrapper() + .select("date, sum(income_real) as income_real") + .lambda(); + if (StringUtils.hasText(query.code)) { + MatrixApp app = appMapper.queryByCode(query.code); + if (app == null) { + return new ArrayList<>(); + } + wrapper.eq(MatrixAdvAggregationSub::getAppId, app.getId()); + } else { + List appIds = adminService.getAdminAppIds(adminId); + if (CollectionUtils.isEmpty(appIds)) { + return new ArrayList<>(); + } + wrapper.in(MatrixAdvAggregationSub::getAppId, appIds); + } + if (query.platform != null) { + wrapper.eq(MatrixAdvAggregationSub::getPlatform, query.platform); + } + wrapper.eq(MatrixAdvAggregationSub::getAdminId, adminId); + int end = DateUtil.datePlus(DateUtil.currentDate(), -1), start = DateUtil.datePlus(end, -30); + if (!CollectionUtils.isEmpty(query.date)) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + try { + Date beginDate = format.parse(query.date.get(0)), endDate = format.parse(query.date.get(1)); + start = DateUtil.long2Number(beginDate.getTime()); + end = DateUtil.long2Number(endDate.getTime()); + } catch (ParseException e) { + throw new PaganiException(PaganiExceptionCode.ILLEGAL_REQUEST, "时间范围有误"); + } + } + wrapper.between(MatrixAdvAggregationSub::getDate, start, end); + wrapper.groupBy(MatrixAdvAggregationSub::getDate); + wrapper.orderByAsc(MatrixAdvAggregationSub::getDate); + List data = subMapper.selectList(wrapper); + Map map = data.stream().collect(Collectors.toMap(MatrixAdvAggregationSub::getDate, x -> x)); + List res = new ArrayList<>(); + for (int d = start; d <= end; d = DateUtil.datePlus(d, 1)) { + MatrixAdvAggregationSub sub = map.get(d); + DateIncome each = new DateIncome(); + each.date = d; + if (sub != null) { + each.income = sub.getIncomeReal(); + } + res.add(each); + } + return res; + } + + @Scheduled(cron = "0 30 1 * * ?") + public void dailyCalc() { + int yesterday = DateUtil.datePlus(DateUtil.currentDate(), -1); + calcTargetDate(yesterday); + } + + public void calcTargetDate(int targetDate) { + long dateBegin = DateUtil.getDayBeginTimestamp(DateUtil.date2Long(targetDate)); + long dateEnd = dateBegin + 86400_000 - 1; + LambdaQueryWrapper query = new QueryWrapper() + .select("admin_id, app_id, platform, sum(ecpm) as ecpm") + .lambda(); + query.between(MatrixAdvRecord::getCreatedAt, new Timestamp(dateBegin), new Timestamp(dateEnd)); + query.eq(MatrixAdvRecord::getAdvType, 3);//设备主只算激励视频 + query.gt(MatrixAdvRecord::getAdminId, 0); + query.groupBy(MatrixAdvRecord::getAdminId,MatrixAdvRecord::getAppId, MatrixAdvRecord::getPlatform); + List records = advRecordMapper.selectList(query); + List aggregations = new ArrayList<>(); + List adminList = adminMapper.selectList(null); + Map adminMap = adminList.stream().collect(Collectors.toMap(x->x.getId(), x->x)); + for (MatrixAdvRecord r : records) { + MatrixAdvAggregationSub aggregation = new MatrixAdvAggregationSub(); + aggregation.setAdminId(r.getAdminId()); + aggregation.setAppId(r.getAppId()); + aggregation.setPlatform(r.getPlatform()); + aggregation.setDate(targetDate); + aggregation.setIncome(r.getEcpm()); + aggregations.add(aggregation); + aggregation.setIncomeRate(adminMap.get(r.getAdminId()).getIncomeRate()); + aggregation.setIncomeReal(aggregation.getIncomeRate() * r.getEcpm() / 100); + } + if (CollectionUtils.isEmpty(aggregations)) { + return; + } + subMapper.insertBatch(aggregations); + } } diff --git a/game-service/src/main/java/awesome/group/game/service/AdminService.java b/game-service/src/main/java/awesome/group/game/service/AdminService.java index 3a75024..b36a4fa 100644 --- a/game-service/src/main/java/awesome/group/game/service/AdminService.java +++ b/game-service/src/main/java/awesome/group/game/service/AdminService.java @@ -366,7 +366,7 @@ public class AdminService { return bo; } - private List getAdminAppIds(int adminId) { + public List getAdminAppIds(int adminId) { MatrixAdmin admin = adminMapper.selectById(adminId); if (admin.getRole() == SUPER_ADMIN) { return appMapper.selectList(null).stream().map(MatrixApp::getId).toList(); diff --git a/game-service/src/main/java/awesome/group/game/service/AggregationSubService.java b/game-service/src/main/java/awesome/group/game/service/AggregationSubService.java new file mode 100644 index 0000000..79d33b2 --- /dev/null +++ b/game-service/src/main/java/awesome/group/game/service/AggregationSubService.java @@ -0,0 +1,34 @@ +package awesome.group.game.service; + +import awesome.group.game.dao.bean.MatrixAdvAggregationSub; +import awesome.group.game.dao.mapper.MatrixAdvAggregationSubMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +@Service +public class AggregationSubService { + @Resource + private MatrixAdvAggregationSubMapper subMapper; + + public long getIncome(int adminId, List appIds, int startDate, int endDate) { + if (CollectionUtils.isEmpty(appIds)) { + return 0L; + } + LambdaQueryWrapper query = new QueryWrapper() + .select("sum(income_real) as income").lambda(); + query.in(MatrixAdvAggregationSub::getAppId, appIds); + query.between(MatrixAdvAggregationSub::getDate, startDate, endDate); + query.eq(MatrixAdvAggregationSub::getAdminId, adminId); + MatrixAdvAggregationSub data = subMapper.selectOne(query); + if (data == null) { + return 0L; + } + return data.getIncome(); + } + +} diff --git a/game-service/src/test/java/awesome/group/game/service/AdminServiceTest.java b/game-service/src/test/java/awesome/group/game/service/AdminServiceTest.java index 84612f7..cfe73a7 100644 --- a/game-service/src/test/java/awesome/group/game/service/AdminServiceTest.java +++ b/game-service/src/test/java/awesome/group/game/service/AdminServiceTest.java @@ -4,17 +4,18 @@ import awesome.group.game.service.util.DateUtil; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import static org.junit.jupiter.api.Assertions.*; - class AdminServiceTest extends BaseTest{ @Autowired private AdminService adminService; + @Autowired + private AdminDeviceService deviceService; + @Test void calcTargetDate() { - int date = 20240401; + int date = 20240315; for(int i = 1; i < 60; i ++) { - adminService.calcTargetDate(date); + deviceService.calcTargetDate(date); date = DateUtil.datePlus(date, -1); } } diff --git a/game-web/src/main/java/awesome/group/game/web/rest/matrix/DeviceController.java b/game-web/src/main/java/awesome/group/game/web/rest/matrix/DeviceController.java index cf0c6e5..e1abc97 100644 --- a/game-web/src/main/java/awesome/group/game/web/rest/matrix/DeviceController.java +++ b/game-web/src/main/java/awesome/group/game/web/rest/matrix/DeviceController.java @@ -2,6 +2,9 @@ package awesome.group.game.web.rest.matrix; import awesome.group.game.dao.bean.MatrixAdminDevice; import awesome.group.game.service.AdminDeviceService; +import awesome.group.game.service.bo.DateIncome; +import awesome.group.game.service.bo.IncomeQuery; +import awesome.group.game.service.bo.OverviewBo; import awesome.group.game.service.common.response.R; import awesome.group.game.web.RequestContext; import awesome.group.game.web.aop.RestApi; @@ -31,4 +34,23 @@ public class DeviceController { adminDeviceService.setStatusOffline(RequestContext.getAdminID(), deviceId); return new R<>("ok"); } + + @GetMapping("/incomeOverview") + @RestApi + public R incomeOverview(@RequestParam(required = false) String appCode) { + return new R<>(R.CODE_SUCCESS, "ok", adminDeviceService.incomeOverview(RequestContext.getAdminID(), appCode)); + } + + @PostMapping("/incomeDaily") + @RestApi + public R> incomeDaily(@RequestBody IncomeQuery query) { + return new R<>(R.CODE_SUCCESS, "ok", adminDeviceService.incomeDaily(query, RequestContext.getAdminID())); + } + + @GetMapping("/calc") + @RestApi + public R calc(@RequestParam Integer date) { + adminDeviceService.calcTargetDate(date); + return new R<>(); + } }