diff --git a/src/main/java/com/ai/da/common/task/PaymentTask.java b/src/main/java/com/ai/da/common/task/PaymentTask.java index 53381e2a..b780b3a2 100644 --- a/src/main/java/com/ai/da/common/task/PaymentTask.java +++ b/src/main/java/com/ai/da/common/task/PaymentTask.java @@ -102,6 +102,12 @@ public class PaymentTask { affiliateService.updateAffiliateInfoWithPayment(); } + // 定时同步(每分钟一次) + @Scheduled(fixedRate = 60000) + public void syncLinkViewCountToDB(){ + affiliateService.syncLinkViewCountToDB(); + } + // @Scheduled(cron = "0 0 8 28-31 * ?") public void commissionSummaryReminder(){ // 每个月末的最后一天的早上八点执行 diff --git a/src/main/java/com/ai/da/common/utils/RedisUtil.java b/src/main/java/com/ai/da/common/utils/RedisUtil.java index 526d601b..8a925732 100644 --- a/src/main/java/com/ai/da/common/utils/RedisUtil.java +++ b/src/main/java/com/ai/da/common/utils/RedisUtil.java @@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.util.internal.StringUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.RedisTemplate; @@ -379,6 +380,15 @@ public class RedisUtil { return redisTemplate.opsForValue().increment(key, 0); } + public Long getAndSetKey(String key, Long count) { + try { + String val = redisTemplate.opsForValue().getAndSet(key, count.toString()); + return StringUtil.isNullOrEmpty(val) ? 0L : Long.parseLong(val); + } catch (NumberFormatException e) { + return 0L; // 或 throw new BusinessException("非法的数值格式"); + } + } + /** * 记录任务的耗时到Redis * @param taskKey 任务标识,如 "taskA" diff --git a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java index f6474c01..ac23b9d7 100644 --- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java +++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java @@ -762,7 +762,7 @@ public class SendEmailUtil { try { String merchantEmail = "kimwong@code-create.com.hk"; String developer = "xupei3360@163.com"; - String[] receiverEmail = {merchantEmail, developer}; + String[] receiverEmail = {/*merchantEmail,*/ developer}; Credential cred = new Credential(SECRET_ID, SECRET_KEy); // 实例化一个http选项,可选的,没有特殊需求可以跳过 HttpProfile httpProfile = new HttpProfile(); @@ -932,7 +932,7 @@ public class SendEmailUtil { req.setFromEmailAddress(SEND_ADDRESS); String merchantEmail = "kimwong@code-create.com.hk"; String developerEmail = "xupei@code-create.com.hk"; - req.setDestination(new String[]{merchantEmail, developerEmail}); + req.setDestination(new String[]{/*merchantEmail,*/ developerEmail}); Template template = new Template(); req.setSubject("New Credit Purchase Order"); template.setTemplateID(CREDITS_PURCHASE_MERCHANT); diff --git a/src/main/java/com/ai/da/controller/AffiliateController.java b/src/main/java/com/ai/da/controller/AffiliateController.java index 7b1b63f3..853580bf 100644 --- a/src/main/java/com/ai/da/controller/AffiliateController.java +++ b/src/main/java/com/ai/da/controller/AffiliateController.java @@ -4,17 +4,24 @@ package com.ai.da.controller; import com.ai.da.common.response.PageBaseResponse; import com.ai.da.common.response.Response; import com.ai.da.model.dto.AffiliateQueryDTO; +import com.ai.da.model.dto.EditReferralDTO; +import com.ai.da.model.dto.ReferralPageQueryDTO; import com.ai.da.model.vo.AffiliateInvitationDetailsVO; import com.ai.da.model.vo.AffiliateVO; +import com.ai.da.model.vo.ReferralPageQueryVO; import com.ai.da.service.AffiliateService; +import com.ai.da.service.ReferralService; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; +import java.math.BigDecimal; +import java.util.List; @Slf4j @RestController @@ -25,6 +32,9 @@ public class AffiliateController { @Resource private AffiliateService affiliateService; + @Resource + private ReferralService referralService; + @ApiOperation(value = "注册成为affiliate") @GetMapping("/registration") public Response completeGuidance(@RequestParam(value = "promotionMethod", required = false) String promotionMethod) { @@ -45,7 +55,7 @@ public class AffiliateController { @ApiOperation(value = "获取个人佣金图表数据") @GetMapping("/getPersonalMonthlyIncome") - public Response getPersonalMonthlyIncome(@RequestParam("year")int year) { + public Response getPersonalMonthlyIncome(@RequestParam("year")int year) { return Response.success(affiliateService.getPersonalMonthlyIncome(year)); } @@ -86,9 +96,29 @@ public class AffiliateController { @ApiOperation(value = "获取每个affiliate产生的收入") @PostMapping("/getEachAffiliateGeneratedRevenue") - public Response> getEachAffiliateGeneratedRevenue(@RequestBody AffiliateQueryDTO affiliateQueryDTO) { + public Response> getEachAffiliateGeneratedRevenue(@Validated @RequestBody AffiliateQueryDTO affiliateQueryDTO) { return Response.success(affiliateService.getEachAffiliateGeneratedRevenue(affiliateQueryDTO)); } + @ApiOperation(value = "获取所有的referral") + @PostMapping("/getReferrals") + public Response> getReferrals(@RequestBody ReferralPageQueryDTO referralPageQueryDTO) { + return Response.success(referralService.queryByPage(referralPageQueryDTO)); + } + + @ApiOperation(value = "编辑单个referral") + @PostMapping("/editReferral") + public Response editReferral(@Validated @RequestBody EditReferralDTO editReferralDTO) { + referralService.editReferral(editReferralDTO); + return Response.success(); + } + + @ApiOperation(value = "(批量)删除referral") + @PostMapping("/batchDeleteReferral") + public Response batchDeleteReferral(@RequestBody List idList) { + referralService.deleteReferral(idList); + return Response.success(); + } + } diff --git a/src/main/java/com/ai/da/mapper/primary/AffiliateIncomeMapper.java b/src/main/java/com/ai/da/mapper/primary/AffiliateIncomeMapper.java deleted file mode 100644 index d0a7d6ce..00000000 --- a/src/main/java/com/ai/da/mapper/primary/AffiliateIncomeMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.ai.da.mapper.primary; - -import com.ai.da.mapper.primary.entity.AffiliateIncome; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; - -import java.util.List; -import java.util.Map; - -public interface AffiliateIncomeMapper extends BaseMapper { - - List> getPersonalMonthlyIncome(Long affiliateAccountId, int year); - - List> getMonthlyAffiliateIncome(int year, int month); -} diff --git a/src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java b/src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java index 1a53ca9f..7454d50f 100644 --- a/src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java +++ b/src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java @@ -12,7 +12,7 @@ public interface AffiliateMapper extends BaseMapper { Map getMonthlyApprovedAffiliate(int year, int month); List getAffiliateList(String status, String startTime, String endTime, - String order, Long affiliateId, int size, int offset); + String order, Long affiliateId, String sortField, int size, int offset); int queryAffiliateTotalCount(String status, String startTime, String endTime, Long affiliateId); diff --git a/src/main/java/com/ai/da/mapper/primary/ReferralMapper.java b/src/main/java/com/ai/da/mapper/primary/ReferralMapper.java new file mode 100644 index 00000000..47e8e5bc --- /dev/null +++ b/src/main/java/com/ai/da/mapper/primary/ReferralMapper.java @@ -0,0 +1,32 @@ +package com.ai.da.mapper.primary; + +import com.ai.da.mapper.primary.entity.Referral; +import com.ai.da.model.vo.ReferralPageQueryVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +public interface ReferralMapper extends BaseMapper { + // 使用自定义SQL进行分页查询 + IPage selectReferralWithAffiliate(Page page, @Param("ew") Wrapper wrapper); + + void batchDeleteByIds(List ids); + + BigDecimal sumAmount( + Long affiliateId, + List statusList, // 改为 List 类型 + LocalDateTime startTime, // 开始时间(可选) + LocalDateTime endTime // 结束时间(可选) + ); + + List> getPersonalMonthlyIncome(Long affiliateAccountId, int year); + + List> getMonthlyAffiliateIncome(int year, int month); +} diff --git a/src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java b/src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java index 5b9b42d4..4fccea61 100644 --- a/src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java +++ b/src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java @@ -22,7 +22,8 @@ public class Affiliate extends BaseEntity{ private Float unpaidEarnings = 0.00F; - private Integer visits = 0; + // 邀请链接的访问人数,每分钟从redis同步到数据库 + private Long visits = 0L; private Boolean approved = false; diff --git a/src/main/java/com/ai/da/mapper/primary/entity/Referral.java b/src/main/java/com/ai/da/mapper/primary/entity/Referral.java new file mode 100644 index 00000000..2d4bb0e2 --- /dev/null +++ b/src/main/java/com/ai/da/mapper/primary/entity/Referral.java @@ -0,0 +1,39 @@ +package com.ai.da.mapper.primary.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@TableName("t_referral") +public class Referral extends BaseEntity{ + + // 佣金 + private BigDecimal commission; + + private Long affiliateId; + + // affiliate的accountId + private Long affiliateAccountId; + + // 产生当前订单的受邀者accountId + private Long inviteeAccountId; + + // 当前订单的总金额(订阅金额) + private BigDecimal amount; + + // 产生该笔佣金时的佣金百分比 + private Float commissionPercent; + + // 产生该笔佣金的订单(payment_info id) + private Long paymentInfoId; + + // 状态 Pending || Accept || Rejected || Paid || Unpaid + private String status; + + private Integer isDeleted = 0; + + private LocalDateTime paymentTime; +} diff --git a/src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java b/src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java index 79fac3e7..54fbf7fc 100644 --- a/src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java +++ b/src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java @@ -5,6 +5,8 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; +import javax.validation.constraints.NotBlank; + @EqualsAndHashCode(callSuper = true) @Data @ApiModel("查询affiliate列表") @@ -15,6 +17,10 @@ public class AffiliateQueryDTO extends TimeQueryBaseDTO{ @ApiModelProperty("推广者id") private Long affiliateId; + @NotBlank(message = "orderBy cannot be empty") + @ApiModelProperty("目前允许按id, createTime, totalIncome进行排序") + private String orderBy; + @ApiModelProperty("按时间 DESC 降序 || ASC 升序") private String order = "ASC"; diff --git a/src/main/java/com/ai/da/model/dto/EditReferralDTO.java b/src/main/java/com/ai/da/model/dto/EditReferralDTO.java new file mode 100644 index 00000000..6ba956d4 --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/EditReferralDTO.java @@ -0,0 +1,18 @@ +package com.ai.da.model.dto; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.math.BigDecimal; + +@Data +public class EditReferralDTO { + @NotNull(message = "referral id cannot be empty") + private Long id; + + private BigDecimal amount; + + @Pattern(regexp = "Paid|Unpaid|Accept|Rejected", message = "状态必须是Paid/Unpaid/Accept/Rejected") + private String status; +} diff --git a/src/main/java/com/ai/da/model/dto/ReferralPageQueryDTO.java b/src/main/java/com/ai/da/model/dto/ReferralPageQueryDTO.java new file mode 100644 index 00000000..0708bd6c --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/ReferralPageQueryDTO.java @@ -0,0 +1,13 @@ +package com.ai.da.model.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class ReferralPageQueryDTO extends TimeQueryBaseDTO { + + private Long affiliateId; + + private String status; +} diff --git a/src/main/java/com/ai/da/model/enums/StyleEnum.java b/src/main/java/com/ai/da/model/enums/StyleEnum.java index dcd62fb4..8520296e 100644 --- a/src/main/java/com/ai/da/model/enums/StyleEnum.java +++ b/src/main/java/com/ai/da/model/enums/StyleEnum.java @@ -13,7 +13,7 @@ public enum StyleEnum { LOLITA("洛丽塔", "Lolita"), Y2K("Y2K", "Y2K"), BUSINESS("商务风", "Business"), - MERLAD("美拉德", "Merlad"), + MERLAD("梅拉德", "Merlad"), OUTDOOR_FUNCTIONAL("户外机能", "Outdoor Functional"), ROCK("摇滚", "Rock"), DOPAMINE("多巴胺", "Dopamine"), diff --git a/src/main/java/com/ai/da/model/vo/AffiliateInvitationDetailsVO.java b/src/main/java/com/ai/da/model/vo/AffiliateInvitationDetailsVO.java index 4a9cdf54..0aa0a53c 100644 --- a/src/main/java/com/ai/da/model/vo/AffiliateInvitationDetailsVO.java +++ b/src/main/java/com/ai/da/model/vo/AffiliateInvitationDetailsVO.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.math.BigDecimal; import java.time.LocalDateTime; @Data @@ -16,9 +17,9 @@ public class AffiliateInvitationDetailsVO { private String username; - private Float firstSubscriptionPaymentAmount; + private BigDecimal firstSubscriptionPaymentAmount; - private Float commission; + private BigDecimal commission; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") private LocalDateTime time; diff --git a/src/main/java/com/ai/da/model/vo/AffiliateVO.java b/src/main/java/com/ai/da/model/vo/AffiliateVO.java index eedf5481..fe44f3eb 100644 --- a/src/main/java/com/ai/da/model/vo/AffiliateVO.java +++ b/src/main/java/com/ai/da/model/vo/AffiliateVO.java @@ -10,7 +10,7 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class AffiliateVO extends Affiliate { - private Long linkViewCount; +// private Long linkViewCount; private String username; diff --git a/src/main/java/com/ai/da/model/vo/ReferralPageQueryVO.java b/src/main/java/com/ai/da/model/vo/ReferralPageQueryVO.java new file mode 100644 index 00000000..abe04074 --- /dev/null +++ b/src/main/java/com/ai/da/model/vo/ReferralPageQueryVO.java @@ -0,0 +1,11 @@ +package com.ai.da.model.vo; + +import com.ai.da.mapper.primary.entity.Referral; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class ReferralPageQueryVO extends Referral { + private String affiliateName; +} diff --git a/src/main/java/com/ai/da/service/AffiliateService.java b/src/main/java/com/ai/da/service/AffiliateService.java index e34a172b..f75111f5 100644 --- a/src/main/java/com/ai/da/service/AffiliateService.java +++ b/src/main/java/com/ai/da/service/AffiliateService.java @@ -8,6 +8,8 @@ import com.ai.da.model.vo.AffiliateVO; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; +import java.math.BigDecimal; + public interface AffiliateService extends IService { Boolean registerAsAnAffiliate(String promotionMethod); @@ -16,7 +18,7 @@ public interface AffiliateService extends IService { AffiliateVO personalAffiliateCenter(); - double[] getPersonalMonthlyIncome(int year); + BigDecimal[] getPersonalMonthlyIncome(int year); Boolean applicationApproval(Long id, Boolean isApproved, Float commission); @@ -26,6 +28,8 @@ public interface AffiliateService extends IService { Boolean affiliateLinkViewsIncrease(Long id); + void syncLinkViewCountToDB(); + IPage getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO); Affiliate getByAccountId(Long accountId); diff --git a/src/main/java/com/ai/da/service/ReferralService.java b/src/main/java/com/ai/da/service/ReferralService.java new file mode 100644 index 00000000..537033f0 --- /dev/null +++ b/src/main/java/com/ai/da/service/ReferralService.java @@ -0,0 +1,19 @@ +package com.ai.da.service; + +import com.ai.da.mapper.primary.entity.Referral; +import com.ai.da.model.dto.EditReferralDTO; +import com.ai.da.model.dto.ReferralPageQueryDTO; +import com.ai.da.model.vo.ReferralPageQueryVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +public interface ReferralService extends IService { + + IPage queryByPage(ReferralPageQueryDTO referralPageQueryDTO); + + void editReferral(EditReferralDTO editReferralDTO); + + void deleteReferral(List idList); +} diff --git a/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java index 6ff66865..5c8fabe0 100644 --- a/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java @@ -8,10 +8,7 @@ import com.ai.da.common.response.ResultEnum; import com.ai.da.common.utils.CopyUtil; import com.ai.da.common.utils.RedisUtil; import com.ai.da.common.utils.SendEmailUtil; -import com.ai.da.mapper.primary.AffiliateIncomeMapper; -import com.ai.da.mapper.primary.AffiliateMapper; -import com.ai.da.mapper.primary.ProductCouponsMapper; -import com.ai.da.mapper.primary.SubscriptionInfoMapper; +import com.ai.da.mapper.primary.*; import com.ai.da.mapper.primary.entity.*; import com.ai.da.model.dto.AffiliateEmailParamsDTO; import com.ai.da.model.dto.AffiliateQueryDTO; @@ -20,6 +17,7 @@ import com.ai.da.model.vo.AffiliateVO; import com.ai.da.model.vo.AuthPrincipalVo; import com.ai.da.service.*; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -35,6 +33,8 @@ import java.time.LocalDateTime; import java.util.*; import java.util.function.Function; +import static com.ai.da.common.utils.RedisUtil.AFFILIATE_LINK_VIEW_KEY; + @Service @Slf4j public class AffiliateServiceImpl extends ServiceImpl implements AffiliateService { @@ -48,7 +48,9 @@ public class AffiliateServiceImpl extends ServiceImpl {}", affiliateQueryDTO.toString()); int offset = (affiliateQueryDTO.getPage() - 1) * affiliateQueryDTO.getSize(); + String orderBy = affiliateQueryDTO.getOrderBy() == null ? "create_time" : + affiliateQueryDTO.getOrderBy().equals("id") ? "id" : + affiliateQueryDTO.getOrderBy().equals("totalIncome") ? "total_earnings" : + "create_time"; List affiliateList = baseMapper.getAffiliateList(affiliateQueryDTO.getStatus(), affiliateQueryDTO.getStartTime(), affiliateQueryDTO.getEndTime(), affiliateQueryDTO.getOrder(), affiliateQueryDTO.getAffiliateId(), + orderBy, affiliateQueryDTO.getSize(), offset ); @@ -133,17 +140,17 @@ public class AffiliateServiceImpl extends ServiceImpl> personalMonthlyIncome = affiliateIncomeMapper.getPersonalMonthlyIncome(accountId, year); - double[] commissions = new double[12]; + List> personalMonthlyIncome = referralMapper.getPersonalMonthlyIncome(accountId, year); + BigDecimal[] commissions = new BigDecimal[12]; + Arrays.fill(commissions, BigDecimal.ZERO); personalMonthlyIncome.forEach(income -> { int month = Integer.parseInt(income.get("yearMonth").toString()); - commissions[month-1] = (double)income.get("totalCommission"); + commissions[month-1] = (BigDecimal) income.get("totalCommission"); }); return commissions; @@ -206,10 +213,14 @@ public class AffiliateServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); + queryWrapper.lambda().eq(PaymentInfo::getTradeState, "paid") + .and(wrapper -> wrapper + .eq(PaymentInfo::getType,"new") + .or() + .eq(PaymentInfo::getType, "manual")); if (!StringUtil.isNullOrEmpty(lastTime)){ - queryWrapper.gt("create_time", lastTime); + queryWrapper.lambda().gt(PaymentInfo::getCreateTime, lastTime); } - queryWrapper.eq("type","new").eq("trade_state", "paid"); List paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper); if (!paymentInfos.isEmpty()){ @@ -232,33 +243,41 @@ public class AffiliateServiceImpl extends ServiceImpl 0){ // 分配新用户首次订阅所付费用 预设的佣金比例 作为佣金 + // 逻辑修改。只有当该笔referral被审批通过后才被计算为推广者的佣金 BigDecimal commission = BigDecimal.valueOf(payerTotal).multiply(BigDecimal.valueOf(affiliate.getCommissionPercent() / 100)); - BigDecimal monthlyEarning = BigDecimal.valueOf(affiliate.getMonthlyEarnings()).add(commission); + /*BigDecimal monthlyEarning = BigDecimal.valueOf(affiliate.getMonthlyEarnings()).add(commission); BigDecimal unpaidEarnings = BigDecimal.valueOf(affiliate.getUnpaidEarnings()).add(commission); int visits = affiliate.getVisits() + 1; affiliate.setMonthlyEarnings(monthlyEarning.floatValue()); affiliate.setUnpaidEarnings(unpaidEarnings.floatValue()); affiliate.setVisits(visits); affiliate.setUpdateTime(LocalDateTime.now()); - baseMapper.updateById(affiliate); + baseMapper.updateById(affiliate);*/ orderInfo.setIsCommissionCalculated((byte)1); - // 4、添加到t_affiliate_income - AffiliateIncome affiliateIncome = new AffiliateIncome(); - affiliateIncome.setAffiliateId(affiliate.getId()); - affiliateIncome.setAffiliateAccountId(affiliate.getAccountId()); - affiliateIncome.setInviteeAccountId(accountId); - affiliateIncome.setAmount(payerTotal); - affiliateIncome.setPaymentInfoId(paymentInfo.getId()); - affiliateIncome.setPaymentTime(paymentInfo.getCreateTime()); - affiliateIncome.setCommission(commission.floatValue()); - affiliateIncome.setCreateTime(LocalDateTime.now()); - affiliateIncomeMapper.insert(affiliateIncome); + // 4、添加到referral + Referral referral = new Referral(); + referral.setAffiliateId(affiliate.getId()); + referral.setAffiliateAccountId(affiliate.getAccountId()); + referral.setInviteeAccountId(accountId); + referral.setAmount(BigDecimal.valueOf(payerTotal)); + referral.setPaymentInfoId(paymentInfo.getId()); + referral.setPaymentTime(paymentInfo.getCreateTime()); + referral.setCommission(BigDecimal.valueOf(commission.floatValue())); + referral.setCommissionPercent(affiliate.getCommissionPercent()); + referral.setStatus("Pending"); + referral.setCreateTime(LocalDateTime.now()); + int insert = referralService.getBaseMapper().insert(referral); + log.info("向Referral中插入 {} 条数据", insert); } } orderInfo.setIsFirstSubscription((byte)1); @@ -279,6 +298,30 @@ public class AffiliateServiceImpl extends ServiceImpl affiliateList = baseMapper.selectList(new QueryWrapper().lambda().eq(Affiliate::getStatus, "Active")); + + // 2、获取redis中各自的viewCount + for (Affiliate affiliate : affiliateList){ + String redisKey = AFFILIATE_LINK_VIEW_KEY + affiliate.getId(); + + // 原子性获取并重置为0 + Long redisCount = redisUtil.getAndSetKey(redisKey, 0L); + + if (redisCount != null && redisCount > 0) { + // 累加到数据库 + baseMapper.update( + null, + new UpdateWrapper() + .setSql("visits = visits + " + redisCount) + .set("update_time", LocalDateTime.now()) + .eq("id", affiliate.getId()) + ); + } + } + } + // 查看每个affiliate带来收入的详情 @Override public IPage getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO) { @@ -286,14 +329,14 @@ public class AffiliateServiceImpl extends ServiceImpl affiliateIncomeQueryWrapper = new QueryWrapper<>(); - affiliateIncomeQueryWrapper + QueryWrapper referralQueryWrapper = new QueryWrapper<>(); + referralQueryWrapper .gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime()) .lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime()) .eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "affiliate_id", affiliateQueryDTO.getAffiliateId()) .orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time"); - IPage affiliateIncomePage = affiliateIncomeMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), affiliateIncomeQueryWrapper); - return affiliateIncomePage.convert((Function) affiliateIncome -> { + IPage affiliateIncomePage = referralMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), referralQueryWrapper); + return affiliateIncomePage.convert((Function) affiliateIncome -> { AffiliateInvitationDetailsVO affiliateInvitationDetailsVO = CopyUtil.copyObject(affiliateIncome, AffiliateInvitationDetailsVO.class); affiliateInvitationDetailsVO.setAccountId(affiliateIncome.getInviteeAccountId()); affiliateInvitationDetailsVO.setUsername(accountService.getBaseMapper().selectById(affiliateIncome.getInviteeAccountId()).getUserName()); @@ -312,14 +355,16 @@ public class AffiliateServiceImpl extends ServiceImpl> monthlyAffiliateIncome = affiliateIncomeMapper.getMonthlyAffiliateIncome(year, month); + List> monthlyAffiliateIncome = referralMapper.getMonthlyAffiliateIncome(year, month); // 1、总收入(近一个月通过affiliate产生的收入),未支付的金额 affiliate表中unpaid的总和 - Double totalAmount = 0.0; - Double totalCommission = 0.0; + BigDecimal totalAmount = BigDecimal.ZERO; + BigDecimal paidCommission = BigDecimal.ZERO; + BigDecimal unpaidCommission = BigDecimal.ZERO; if (!monthlyAffiliateIncome.isEmpty()){ Map monthlyIncome = monthlyAffiliateIncome.get(0); - totalAmount = (Double) monthlyIncome.get("totalAmount"); - totalCommission = (Double) monthlyIncome.get("totalCommission"); + totalAmount = (BigDecimal) monthlyIncome.get("totalAmount"); + paidCommission = (BigDecimal) monthlyIncome.get("paidCommission"); + unpaidCommission = (BigDecimal) monthlyIncome.get("unpaidCommission"); } // 2、本月新注册的Affiliate @@ -329,12 +374,12 @@ public class AffiliateServiceImpl extends ServiceImpl implements ReferralService { + + @Resource + private AffiliateService affiliateService; + + // Referral Recap + // 当有用户通过链接成功订阅AiDA后。这里应该要出现一笔待支付给affiliate的记录 + // 每一笔交易需要管理员Accept才能被算为佣金(有审批这个过程) + + // 查询所有referral (分页查) + // 允许按推广人用户名、记录产生时间、记录状态分页查询 + public IPage queryByPage(ReferralPageQueryDTO referralPageQueryDTO) { + // 1. 构建查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + + if (referralPageQueryDTO.getAffiliateId() != null && referralPageQueryDTO.getAffiliateId() != 0L) { + queryWrapper.eq("r.affiliate_id", referralPageQueryDTO.getAffiliateId()); + } + if (!StringUtil.isNullOrEmpty(referralPageQueryDTO.getStartTime())) { + queryWrapper.gt("r.create_time", referralPageQueryDTO.getStartTime()); + } + if (!StringUtil.isNullOrEmpty(referralPageQueryDTO.getEndTime())) { + queryWrapper.lt("r.create_time", referralPageQueryDTO.getEndTime()); + } + if (!StringUtil.isNullOrEmpty(referralPageQueryDTO.getStatus()) && !"All Types".equals(referralPageQueryDTO.getStatus())) { + queryWrapper.eq("r.status", referralPageQueryDTO.getStatus()); + } + + queryWrapper.eq("r.is_deleted", 0); + + // 2. 执行分页查询(返回的是 ReferralVO) + Page page = new Page<>(referralPageQueryDTO.getPage(), referralPageQueryDTO.getSize()); + return baseMapper.selectReferralWithAffiliate(page, queryWrapper); + } + + // 编辑referral + @Transactional(rollbackFor = Exception.class) + public void editReferral(EditReferralDTO editReferralDTO){ + Referral referral = baseMapper.selectById(editReferralDTO.getId()); + if (Objects.nonNull(editReferralDTO.getAmount()) && editReferralDTO.getAmount().compareTo(referral.getCommission()) != 0){ + log.info("设置referral id为{} 原佣金 {} 设置为 {}", referral.getId(), referral.getAffiliateId(), editReferralDTO.getAmount()); + referral.setCommission(editReferralDTO.getAmount()); + referral.setUpdateTime(LocalDateTime.now()); + } + if (!StringUtil.isNullOrEmpty(editReferralDTO.getStatus()) && !editReferralDTO.getStatus().equals(referral.getStatus())){ + // 如果该笔佣金审批通过,则自动转为未支付状态 + if (editReferralDTO.getStatus().equals("Accept")){ + editReferralDTO.setStatus("Unpaid"); + } + log.info("设置referral id为{} 原状态 {} 设置为 {}", referral.getId(), referral.getStatus(), editReferralDTO.getStatus()); + referral.setStatus(editReferralDTO.getStatus()); + referral.setUpdateTime(LocalDateTime.now()); + } + boolean b = updateById(referral); + + if (b){ + Affiliate affiliate = affiliateService.getBaseMapper().selectById(referral.getAffiliateId()); + + // 如果修改了referral的状态,则需要同步修改affiliate已支付或未支付的金额总数 + // 在平台的总未支付 + BigDecimal unpaid = baseMapper.sumAmount(referral.getAffiliateId(), Collections.singletonList("Unpaid"), null, null); + + // 已支付和未支付的值肯定是同时发生变化 + if (BigDecimal.valueOf(affiliate.getUnpaidEarnings()).compareTo(unpaid) != 0){ + LocalDateTime start = YearMonth.now().atDay(1).atStartOfDay(); // 本月第一天 00:00:00 + LocalDateTime end = YearMonth.now().atEndOfMonth().atTime(23, 59, 59); + // 本月的总收入(包括已支付和未支付) + BigDecimal monthlyEarning = baseMapper.sumAmount(referral.getAffiliateId(), Arrays.asList("Unpaid", "Paid"), start, end); + // 在平台的总收入 + BigDecimal totalEarning = baseMapper.sumAmount(referral.getAffiliateId(), Arrays.asList("Unpaid", "Paid"), null, null); + affiliate.setUnpaidEarnings(Objects.nonNull(unpaid) ? unpaid.floatValue() : 0); + affiliate.setMonthlyEarnings(Objects.nonNull(monthlyEarning) ? monthlyEarning.floatValue() : 0); + affiliate.setTotalEarnings(Objects.nonNull(totalEarning) ? totalEarning.floatValue() : 0); + affiliate.setUpdateTime(LocalDateTime.now()); + affiliateService.getBaseMapper().updateById(affiliate); + } + } + } + + // 批量删除 + public void deleteReferral(List idList){ + if (!idList.isEmpty()){ + baseMapper.batchDeleteByIds(idList); + } + } +} diff --git a/src/main/resources/mapper/primary/AffiliateIncomeMapper.xml b/src/main/resources/mapper/primary/AffiliateIncomeMapper.xml deleted file mode 100644 index bac4a547..00000000 --- a/src/main/resources/mapper/primary/AffiliateIncomeMapper.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - diff --git a/src/main/resources/mapper/primary/AffiliateMapper.xml b/src/main/resources/mapper/primary/AffiliateMapper.xml index e2637ec0..853bf59d 100644 --- a/src/main/resources/mapper/primary/AffiliateMapper.xml +++ b/src/main/resources/mapper/primary/AffiliateMapper.xml @@ -38,12 +38,24 @@ AND f.id = #{affiliateId} - - ORDER BY f.create_time DESC - - - ORDER BY f.create_time ASC - + + + ORDER BY + + f.id + f.total_earnings + f.create_time + f.create_time + + + DESC + ASC + + + + ORDER BY f.create_time ASC + + LIMIT ${size} OFFSET ${offset} diff --git a/src/main/resources/mapper/primary/ReferralMapper.xml b/src/main/resources/mapper/primary/ReferralMapper.xml new file mode 100644 index 00000000..16432b24 --- /dev/null +++ b/src/main/resources/mapper/primary/ReferralMapper.xml @@ -0,0 +1,81 @@ + + + + + + + + + + UPDATE t_referral + SET is_deleted = 1 + WHERE id IN + + #{id} + + + + + + + + + + \ No newline at end of file