TASK: 1.affiliate 新增referral新增、查询、修改以及其他相关的自动结算佣金的功能;2.删除affiliate_income表及相关mapper

This commit is contained in:
2025-08-19 17:44:34 +08:00
parent 552ec828ab
commit caa9985d11
23 changed files with 499 additions and 105 deletions

View File

@@ -102,6 +102,12 @@ public class PaymentTask {
affiliateService.updateAffiliateInfoWithPayment(); affiliateService.updateAffiliateInfoWithPayment();
} }
// 定时同步(每分钟一次)
@Scheduled(fixedRate = 60000)
public void syncLinkViewCountToDB(){
affiliateService.syncLinkViewCountToDB();
}
// @Scheduled(cron = "0 0 8 28-31 * ?") // @Scheduled(cron = "0 0 8 28-31 * ?")
public void commissionSummaryReminder(){ public void commissionSummaryReminder(){
// 每个月末的最后一天的早上八点执行 // 每个月末的最后一天的早上八点执行

View File

@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
@@ -379,6 +380,15 @@ public class RedisUtil {
return redisTemplate.opsForValue().increment(key, 0); 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 * 记录任务的耗时到Redis
* @param taskKey 任务标识,如 "taskA" * @param taskKey 任务标识,如 "taskA"

View File

@@ -762,7 +762,7 @@ public class SendEmailUtil {
try { try {
String merchantEmail = "kimwong@code-create.com.hk"; String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com"; String developer = "xupei3360@163.com";
String[] receiverEmail = {merchantEmail, developer}; String[] receiverEmail = {/*merchantEmail,*/ developer};
Credential cred = new Credential(SECRET_ID, SECRET_KEy); Credential cred = new Credential(SECRET_ID, SECRET_KEy);
// 实例化一个http选项可选的没有特殊需求可以跳过 // 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile(); HttpProfile httpProfile = new HttpProfile();
@@ -932,7 +932,7 @@ public class SendEmailUtil {
req.setFromEmailAddress(SEND_ADDRESS); req.setFromEmailAddress(SEND_ADDRESS);
String merchantEmail = "kimwong@code-create.com.hk"; String merchantEmail = "kimwong@code-create.com.hk";
String developerEmail = "xupei@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(); Template template = new Template();
req.setSubject("New Credit Purchase Order"); req.setSubject("New Credit Purchase Order");
template.setTemplateID(CREDITS_PURCHASE_MERCHANT); template.setTemplateID(CREDITS_PURCHASE_MERCHANT);

View File

@@ -4,17 +4,24 @@ package com.ai.da.controller;
import com.ai.da.common.response.PageBaseResponse; import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response; import com.ai.da.common.response.Response;
import com.ai.da.model.dto.AffiliateQueryDTO; 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.AffiliateInvitationDetailsVO;
import com.ai.da.model.vo.AffiliateVO; 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.AffiliateService;
import com.ai.da.service.ReferralService;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.List;
@Slf4j @Slf4j
@RestController @RestController
@@ -25,6 +32,9 @@ public class AffiliateController {
@Resource @Resource
private AffiliateService affiliateService; private AffiliateService affiliateService;
@Resource
private ReferralService referralService;
@ApiOperation(value = "注册成为affiliate") @ApiOperation(value = "注册成为affiliate")
@GetMapping("/registration") @GetMapping("/registration")
public Response<Boolean> completeGuidance(@RequestParam(value = "promotionMethod", required = false) String promotionMethod) { public Response<Boolean> completeGuidance(@RequestParam(value = "promotionMethod", required = false) String promotionMethod) {
@@ -45,7 +55,7 @@ public class AffiliateController {
@ApiOperation(value = "获取个人佣金图表数据") @ApiOperation(value = "获取个人佣金图表数据")
@GetMapping("/getPersonalMonthlyIncome") @GetMapping("/getPersonalMonthlyIncome")
public Response<double[]> getPersonalMonthlyIncome(@RequestParam("year")int year) { public Response<BigDecimal[]> getPersonalMonthlyIncome(@RequestParam("year")int year) {
return Response.success(affiliateService.getPersonalMonthlyIncome(year)); return Response.success(affiliateService.getPersonalMonthlyIncome(year));
} }
@@ -86,9 +96,29 @@ public class AffiliateController {
@ApiOperation(value = "获取每个affiliate产生的收入") @ApiOperation(value = "获取每个affiliate产生的收入")
@PostMapping("/getEachAffiliateGeneratedRevenue") @PostMapping("/getEachAffiliateGeneratedRevenue")
public Response<IPage<AffiliateInvitationDetailsVO>> getEachAffiliateGeneratedRevenue(@RequestBody AffiliateQueryDTO affiliateQueryDTO) { public Response<IPage<AffiliateInvitationDetailsVO>> getEachAffiliateGeneratedRevenue(@Validated @RequestBody AffiliateQueryDTO affiliateQueryDTO) {
return Response.success(affiliateService.getEachAffiliateGeneratedRevenue(affiliateQueryDTO)); return Response.success(affiliateService.getEachAffiliateGeneratedRevenue(affiliateQueryDTO));
} }
@ApiOperation(value = "获取所有的referral")
@PostMapping("/getReferrals")
public Response<IPage<ReferralPageQueryVO>> getReferrals(@RequestBody ReferralPageQueryDTO referralPageQueryDTO) {
return Response.success(referralService.queryByPage(referralPageQueryDTO));
}
@ApiOperation(value = "编辑单个referral")
@PostMapping("/editReferral")
public Response<String> editReferral(@Validated @RequestBody EditReferralDTO editReferralDTO) {
referralService.editReferral(editReferralDTO);
return Response.success();
}
@ApiOperation(value = "批量删除referral")
@PostMapping("/batchDeleteReferral")
public Response<String> batchDeleteReferral(@RequestBody List<Long> idList) {
referralService.deleteReferral(idList);
return Response.success();
}
} }

View File

@@ -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<AffiliateIncome> {
List<Map<String, Object>> getPersonalMonthlyIncome(Long affiliateAccountId, int year);
List<Map<String, Object>> getMonthlyAffiliateIncome(int year, int month);
}

View File

@@ -12,7 +12,7 @@ public interface AffiliateMapper extends BaseMapper<Affiliate> {
Map<String, Long> getMonthlyApprovedAffiliate(int year, int month); Map<String, Long> getMonthlyApprovedAffiliate(int year, int month);
List<AffiliateVO> getAffiliateList(String status, String startTime, String endTime, List<AffiliateVO> 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); int queryAffiliateTotalCount(String status, String startTime, String endTime, Long affiliateId);

View File

@@ -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<Referral> {
// 使用自定义SQL进行分页查询
IPage<ReferralPageQueryVO> selectReferralWithAffiliate(Page<ReferralPageQueryVO> page, @Param("ew") Wrapper<ReferralPageQueryVO> wrapper);
void batchDeleteByIds(List<Long> ids);
BigDecimal sumAmount(
Long affiliateId,
List<String> statusList, // 改为 List 类型
LocalDateTime startTime, // 开始时间(可选)
LocalDateTime endTime // 结束时间(可选)
);
List<Map<String, Object>> getPersonalMonthlyIncome(Long affiliateAccountId, int year);
List<Map<String, Object>> getMonthlyAffiliateIncome(int year, int month);
}

View File

@@ -22,7 +22,8 @@ public class Affiliate extends BaseEntity{
private Float unpaidEarnings = 0.00F; private Float unpaidEarnings = 0.00F;
private Integer visits = 0; // 邀请链接的访问人数每分钟从redis同步到数据库
private Long visits = 0L;
private Boolean approved = false; private Boolean approved = false;

View File

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

View File

@@ -5,6 +5,8 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@ApiModel("查询affiliate列表") @ApiModel("查询affiliate列表")
@@ -15,6 +17,10 @@ public class AffiliateQueryDTO extends TimeQueryBaseDTO{
@ApiModelProperty("推广者id") @ApiModelProperty("推广者id")
private Long affiliateId; private Long affiliateId;
@NotBlank(message = "orderBy cannot be empty")
@ApiModelProperty("目前允许按id, createTime, totalIncome进行排序")
private String orderBy;
@ApiModelProperty("按时间 DESC 降序 || ASC 升序") @ApiModelProperty("按时间 DESC 降序 || ASC 升序")
private String order = "ASC"; private String order = "ASC";

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ public enum StyleEnum {
LOLITA("洛丽塔", "Lolita"), LOLITA("洛丽塔", "Lolita"),
Y2K("Y2K", "Y2K"), Y2K("Y2K", "Y2K"),
BUSINESS("商务风", "Business"), BUSINESS("商务风", "Business"),
MERLAD("拉德", "Merlad"), MERLAD("拉德", "Merlad"),
OUTDOOR_FUNCTIONAL("户外机能", "Outdoor Functional"), OUTDOOR_FUNCTIONAL("户外机能", "Outdoor Functional"),
ROCK("摇滚", "Rock"), ROCK("摇滚", "Rock"),
DOPAMINE("多巴胺", "Dopamine"), DOPAMINE("多巴胺", "Dopamine"),

View File

@@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Data @Data
@@ -16,9 +17,9 @@ public class AffiliateInvitationDetailsVO {
private String username; 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") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private LocalDateTime time; private LocalDateTime time;

View File

@@ -10,7 +10,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public class AffiliateVO extends Affiliate { public class AffiliateVO extends Affiliate {
private Long linkViewCount; // private Long linkViewCount;
private String username; private String username;

View File

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

View File

@@ -8,6 +8,8 @@ import com.ai.da.model.vo.AffiliateVO;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.math.BigDecimal;
public interface AffiliateService extends IService<Affiliate> { public interface AffiliateService extends IService<Affiliate> {
Boolean registerAsAnAffiliate(String promotionMethod); Boolean registerAsAnAffiliate(String promotionMethod);
@@ -16,7 +18,7 @@ public interface AffiliateService extends IService<Affiliate> {
AffiliateVO personalAffiliateCenter(); AffiliateVO personalAffiliateCenter();
double[] getPersonalMonthlyIncome(int year); BigDecimal[] getPersonalMonthlyIncome(int year);
Boolean applicationApproval(Long id, Boolean isApproved, Float commission); Boolean applicationApproval(Long id, Boolean isApproved, Float commission);
@@ -26,6 +28,8 @@ public interface AffiliateService extends IService<Affiliate> {
Boolean affiliateLinkViewsIncrease(Long id); Boolean affiliateLinkViewsIncrease(Long id);
void syncLinkViewCountToDB();
IPage<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO); IPage<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO);
Affiliate getByAccountId(Long accountId); Affiliate getByAccountId(Long accountId);

View File

@@ -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<Referral> {
IPage<ReferralPageQueryVO> queryByPage(ReferralPageQueryDTO referralPageQueryDTO);
void editReferral(EditReferralDTO editReferralDTO);
void deleteReferral(List<Long> idList);
}

View File

@@ -8,10 +8,7 @@ import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.utils.CopyUtil; import com.ai.da.common.utils.CopyUtil;
import com.ai.da.common.utils.RedisUtil; import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.SendEmailUtil; import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.mapper.primary.AffiliateIncomeMapper; import com.ai.da.mapper.primary.*;
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.entity.*; import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.AffiliateEmailParamsDTO; import com.ai.da.model.dto.AffiliateEmailParamsDTO;
import com.ai.da.model.dto.AffiliateQueryDTO; 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.model.vo.AuthPrincipalVo;
import com.ai.da.service.*; import com.ai.da.service.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 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.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -35,6 +33,8 @@ import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import static com.ai.da.common.utils.RedisUtil.AFFILIATE_LINK_VIEW_KEY;
@Service @Service
@Slf4j @Slf4j
public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate> implements AffiliateService { public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate> implements AffiliateService {
@@ -48,7 +48,9 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
@Resource @Resource
private SubscriptionInfoMapper subscriptionInfoMapper; private SubscriptionInfoMapper subscriptionInfoMapper;
@Resource @Resource
private AffiliateIncomeMapper affiliateIncomeMapper; private ReferralService referralService;
@Resource
private ReferralMapper referralMapper;
@Resource @Resource
private StripeService stripeService; private StripeService stripeService;
@Resource @Resource
@@ -75,7 +77,7 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
// 邮件通知审批者 // 邮件通知审批者
String merchantEmail = "kimwong@code-create.com.hk"; String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com"; String developer = "xupei3360@163.com";
String[] receiverEmail = {merchantEmail, developer}; String[] receiverEmail = {/*merchantEmail, */developer};
SendEmailUtil.affiliateEmailReminder(receiverEmail, new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new"); SendEmailUtil.affiliateEmailReminder(receiverEmail, new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new");
// emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new"); // emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new");
}else { }else {
@@ -88,11 +90,16 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
log.info("parameter => {}", affiliateQueryDTO.toString()); log.info("parameter => {}", affiliateQueryDTO.toString());
int offset = (affiliateQueryDTO.getPage() - 1) * affiliateQueryDTO.getSize(); 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<AffiliateVO> affiliateList = baseMapper.getAffiliateList(affiliateQueryDTO.getStatus(), List<AffiliateVO> affiliateList = baseMapper.getAffiliateList(affiliateQueryDTO.getStatus(),
affiliateQueryDTO.getStartTime(), affiliateQueryDTO.getStartTime(),
affiliateQueryDTO.getEndTime(), affiliateQueryDTO.getEndTime(),
affiliateQueryDTO.getOrder(), affiliateQueryDTO.getOrder(),
affiliateQueryDTO.getAffiliateId(), affiliateQueryDTO.getAffiliateId(),
orderBy,
affiliateQueryDTO.getSize(), affiliateQueryDTO.getSize(),
offset offset
); );
@@ -133,17 +140,17 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
qw.eq("account_id", accountId); qw.eq("account_id", accountId);
Affiliate affiliate = baseMapper.selectOne(qw); Affiliate affiliate = baseMapper.selectOne(qw);
AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class); AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class);
affiliateVO.setLinkViewCount(getAffiliateLinkViewCount(affiliate.getId()));
return affiliateVO; return affiliateVO;
} }
public double[] getPersonalMonthlyIncome(int year){ public BigDecimal[] getPersonalMonthlyIncome(int year){
Long accountId = UserContext.getUserHolder().getId(); Long accountId = UserContext.getUserHolder().getId();
List<Map<String, Object>> personalMonthlyIncome = affiliateIncomeMapper.getPersonalMonthlyIncome(accountId, year); List<Map<String, Object>> personalMonthlyIncome = referralMapper.getPersonalMonthlyIncome(accountId, year);
double[] commissions = new double[12]; BigDecimal[] commissions = new BigDecimal[12];
Arrays.fill(commissions, BigDecimal.ZERO);
personalMonthlyIncome.forEach(income -> { personalMonthlyIncome.forEach(income -> {
int month = Integer.parseInt(income.get("yearMonth").toString()); int month = Integer.parseInt(income.get("yearMonth").toString());
commissions[month-1] = (double)income.get("totalCommission"); commissions[month-1] = (BigDecimal) income.get("totalCommission");
}); });
return commissions; return commissions;
@@ -206,10 +213,14 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
String currentTime = LocalDateTime.now().toString(); String currentTime = LocalDateTime.now().toString();
// 1、查上次更新之后有无新订单 // 1、查上次更新之后有无新订单
QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>(); QueryWrapper<PaymentInfo> 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)){ if (!StringUtil.isNullOrEmpty(lastTime)){
queryWrapper.gt("create_time", lastTime); queryWrapper.lambda().gt(PaymentInfo::getCreateTime, lastTime);
} }
queryWrapper.eq("type","new").eq("trade_state", "paid");
List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper); List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper);
if (!paymentInfos.isEmpty()){ if (!paymentInfos.isEmpty()){
@@ -232,33 +243,41 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
log.info("结算订单id为{}的佣金", orderInfo.getId()); log.info("结算订单id为{}的佣金", orderInfo.getId());
// 3、若有, 直接更新affiliate的所得 // 3、若有, 直接更新affiliate的所得
Affiliate affiliate = baseMapper.selectById(account.getInvitationCode()); Affiliate affiliate = baseMapper.selectById(account.getInvitationCode());
if (Objects.isNull(affiliate)){
log.error("未知affiliate Id:{}, 关联订单:{}", account.getInvitationCode(), orderInfo.getOrderNo());
return;
}
Float payerTotal = paymentInfo.getPayerTotal(); Float payerTotal = paymentInfo.getPayerTotal();
if (payerTotal > 0){ if (payerTotal > 0){
// 分配新用户首次订阅所付费用 预设的佣金比例 作为佣金 // 分配新用户首次订阅所付费用 预设的佣金比例 作为佣金
// 逻辑修改。只有当该笔referral被审批通过后才被计算为推广者的佣金
BigDecimal commission = BigDecimal.valueOf(payerTotal).multiply(BigDecimal.valueOf(affiliate.getCommissionPercent() / 100)); 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); BigDecimal unpaidEarnings = BigDecimal.valueOf(affiliate.getUnpaidEarnings()).add(commission);
int visits = affiliate.getVisits() + 1; int visits = affiliate.getVisits() + 1;
affiliate.setMonthlyEarnings(monthlyEarning.floatValue()); affiliate.setMonthlyEarnings(monthlyEarning.floatValue());
affiliate.setUnpaidEarnings(unpaidEarnings.floatValue()); affiliate.setUnpaidEarnings(unpaidEarnings.floatValue());
affiliate.setVisits(visits); affiliate.setVisits(visits);
affiliate.setUpdateTime(LocalDateTime.now()); affiliate.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(affiliate); baseMapper.updateById(affiliate);*/
orderInfo.setIsCommissionCalculated((byte)1); orderInfo.setIsCommissionCalculated((byte)1);
// 4、添加到t_affiliate_income // 4、添加到referral
AffiliateIncome affiliateIncome = new AffiliateIncome(); Referral referral = new Referral();
affiliateIncome.setAffiliateId(affiliate.getId()); referral.setAffiliateId(affiliate.getId());
affiliateIncome.setAffiliateAccountId(affiliate.getAccountId()); referral.setAffiliateAccountId(affiliate.getAccountId());
affiliateIncome.setInviteeAccountId(accountId); referral.setInviteeAccountId(accountId);
affiliateIncome.setAmount(payerTotal); referral.setAmount(BigDecimal.valueOf(payerTotal));
affiliateIncome.setPaymentInfoId(paymentInfo.getId()); referral.setPaymentInfoId(paymentInfo.getId());
affiliateIncome.setPaymentTime(paymentInfo.getCreateTime()); referral.setPaymentTime(paymentInfo.getCreateTime());
affiliateIncome.setCommission(commission.floatValue()); referral.setCommission(BigDecimal.valueOf(commission.floatValue()));
affiliateIncome.setCreateTime(LocalDateTime.now()); referral.setCommissionPercent(affiliate.getCommissionPercent());
affiliateIncomeMapper.insert(affiliateIncome); referral.setStatus("Pending");
referral.setCreateTime(LocalDateTime.now());
int insert = referralService.getBaseMapper().insert(referral);
log.info("向Referral中插入 {} 条数据", insert);
} }
} }
orderInfo.setIsFirstSubscription((byte)1); orderInfo.setIsFirstSubscription((byte)1);
@@ -279,6 +298,30 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
return redisUtil.getAffiliateLinkViewCount(affiliateId); return redisUtil.getAffiliateLinkViewCount(affiliateId);
} }
public void syncLinkViewCountToDB() {
// 1、获取当前所有激活状态的affiliate
List<Affiliate> affiliateList = baseMapper.selectList(new QueryWrapper<Affiliate>().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<Affiliate>()
.setSql("visits = visits + " + redisCount)
.set("update_time", LocalDateTime.now())
.eq("id", affiliate.getId())
);
}
}
}
// 查看每个affiliate带来收入的详情 // 查看每个affiliate带来收入的详情
@Override @Override
public IPage<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO) { public IPage<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO) {
@@ -286,14 +329,14 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
throw new BusinessException("Please specify the affiliate ID.", ResultEnum.PROMPT.getCode()); throw new BusinessException("Please specify the affiliate ID.", ResultEnum.PROMPT.getCode());
} }
QueryWrapper<AffiliateIncome> affiliateIncomeQueryWrapper = new QueryWrapper<>(); QueryWrapper<Referral> referralQueryWrapper = new QueryWrapper<>();
affiliateIncomeQueryWrapper referralQueryWrapper
.gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime()) .gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime())
.lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime()) .lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime())
.eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "affiliate_id", affiliateQueryDTO.getAffiliateId()) .eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "affiliate_id", affiliateQueryDTO.getAffiliateId())
.orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time"); .orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time");
IPage<AffiliateIncome> affiliateIncomePage = affiliateIncomeMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), affiliateIncomeQueryWrapper); IPage<Referral> affiliateIncomePage = referralMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), referralQueryWrapper);
return affiliateIncomePage.convert((Function<AffiliateIncome, AffiliateInvitationDetailsVO>) affiliateIncome -> { return affiliateIncomePage.convert((Function<Referral, AffiliateInvitationDetailsVO>) affiliateIncome -> {
AffiliateInvitationDetailsVO affiliateInvitationDetailsVO = CopyUtil.copyObject(affiliateIncome, AffiliateInvitationDetailsVO.class); AffiliateInvitationDetailsVO affiliateInvitationDetailsVO = CopyUtil.copyObject(affiliateIncome, AffiliateInvitationDetailsVO.class);
affiliateInvitationDetailsVO.setAccountId(affiliateIncome.getInviteeAccountId()); affiliateInvitationDetailsVO.setAccountId(affiliateIncome.getInviteeAccountId());
affiliateInvitationDetailsVO.setUsername(accountService.getBaseMapper().selectById(affiliateIncome.getInviteeAccountId()).getUserName()); affiliateInvitationDetailsVO.setUsername(accountService.getBaseMapper().selectById(affiliateIncome.getInviteeAccountId()).getUserName());
@@ -312,14 +355,16 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
month = LocalDateTime.now().getMonthValue(); month = LocalDateTime.now().getMonthValue();
} }
List<Map<String, Object>> monthlyAffiliateIncome = affiliateIncomeMapper.getMonthlyAffiliateIncome(year, month); List<Map<String, Object>> monthlyAffiliateIncome = referralMapper.getMonthlyAffiliateIncome(year, month);
// 1、总收入(近一个月通过affiliate产生的收入),未支付的金额 affiliate表中unpaid的总和 // 1、总收入(近一个月通过affiliate产生的收入),未支付的金额 affiliate表中unpaid的总和
Double totalAmount = 0.0; BigDecimal totalAmount = BigDecimal.ZERO;
Double totalCommission = 0.0; BigDecimal paidCommission = BigDecimal.ZERO;
BigDecimal unpaidCommission = BigDecimal.ZERO;
if (!monthlyAffiliateIncome.isEmpty()){ if (!monthlyAffiliateIncome.isEmpty()){
Map<String, Object> monthlyIncome = monthlyAffiliateIncome.get(0); Map<String, Object> monthlyIncome = monthlyAffiliateIncome.get(0);
totalAmount = (Double) monthlyIncome.get("totalAmount"); totalAmount = (BigDecimal) monthlyIncome.get("totalAmount");
totalCommission = (Double) monthlyIncome.get("totalCommission"); paidCommission = (BigDecimal) monthlyIncome.get("paidCommission");
unpaidCommission = (BigDecimal) monthlyIncome.get("unpaidCommission");
} }
// 2、本月新注册的Affiliate // 2、本月新注册的Affiliate
@@ -329,12 +374,12 @@ public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate
AffiliateEmailParamsDTO affiliateEmailParamsDTO = new AffiliateEmailParamsDTO(); AffiliateEmailParamsDTO affiliateEmailParamsDTO = new AffiliateEmailParamsDTO();
affiliateEmailParamsDTO.setTotalProgramRevenue(totalAmount.toString()); affiliateEmailParamsDTO.setTotalProgramRevenue(totalAmount.toString());
affiliateEmailParamsDTO.setNewApprovedAffiliates(count.toString()); affiliateEmailParamsDTO.setNewApprovedAffiliates(count.toString());
affiliateEmailParamsDTO.setUnpaidEarnings(totalCommission.toString()); affiliateEmailParamsDTO.setUnpaidEarnings(String.valueOf(unpaidCommission));
affiliateEmailParamsDTO.setPaidEarnings("0"); affiliateEmailParamsDTO.setPaidEarnings(String.valueOf(paidCommission));
String merchantEmail = "kimwong@code-create.com.hk"; String merchantEmail = "kimwong@code-create.com.hk";
String developer = "xupei3360@163.com"; String developer = "xupei3360@163.com";
String[] receiverEmail = {merchantEmail, developer}; String[] receiverEmail = {/*merchantEmail,*/ developer};
// 邮件通知 // 邮件通知
SendEmailUtil.affiliateEmailReminder(receiverEmail, affiliateEmailParamsDTO, "summary"); SendEmailUtil.affiliateEmailReminder(receiverEmail, affiliateEmailParamsDTO, "summary");
// emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), affiliateEmailParamsDTO, "summary"); // emailService.affiliateEmailReminder(Arrays.asList(/*merchantEmail,*/ developer), affiliateEmailParamsDTO, "summary");

View File

@@ -0,0 +1,116 @@
package com.ai.da.service.impl;
import com.ai.da.mapper.primary.ReferralMapper;
import com.ai.da.mapper.primary.entity.Affiliate;
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.ai.da.service.AffiliateService;
import com.ai.da.service.ReferralService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@Slf4j
@Service
public class ReferralServiceImpl extends ServiceImpl<ReferralMapper, Referral> implements ReferralService {
@Resource
private AffiliateService affiliateService;
// Referral Recap
// 当有用户通过链接成功订阅AiDA后。这里应该要出现一笔待支付给affiliate的记录
// 每一笔交易需要管理员Accept才能被算为佣金有审批这个过程
// 查询所有referral (分页查)
// 允许按推广人用户名、记录产生时间、记录状态分页查询
public IPage<ReferralPageQueryVO> queryByPage(ReferralPageQueryDTO referralPageQueryDTO) {
// 1. 构建查询条件
QueryWrapper<ReferralPageQueryVO> 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<ReferralPageQueryVO> 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<Long> idList){
if (!idList.isEmpty()){
baseMapper.batchDeleteByIds(idList);
}
}
}

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ai.da.mapper.primary.AffiliateIncomeMapper">
<select id="getPersonalMonthlyIncome" resultType="java.util.Map">
SELECT
DATE_FORMAT(payment_time, '%m') as yearMonth,
SUM(commission) AS totalCommission
FROM
t_affiliate_income
WHERE
YEAR(payment_time) = #{year}
and affiliate_account_id = #{affiliateAccountId}
GROUP BY
yearMonth
ORDER BY
yearMonth
</select>
<select id="getMonthlyAffiliateIncome" resultType="java.util.Map">
SELECT
DATE_FORMAT(payment_time, '%m') as yearMonth,
SUM(amount) AS totalAmount,
SUM(commission) AS totalCommission
FROM
t_affiliate_income
WHERE
YEAR(payment_time) = #{year}
and MONTH(payment_time) = #{month}
GROUP BY
yearMonth
ORDER BY
yearMonth
</select>
</mapper>

View File

@@ -38,12 +38,24 @@
AND f.id = #{affiliateId} AND f.id = #{affiliateId}
</if> </if>
</where> </where>
<if test="order != null and order.toUpperCase() == 'DESC'"> <choose>
ORDER BY f.create_time DESC <when test="sortField != null and sortField != ''">
</if> ORDER BY
<if test="order == null or order.toUpperCase() != 'DESC'"> <choose>
ORDER BY f.create_time ASC <when test="sortField == 'id'">f.id</when>
</if> <when test="sortField == 'totalEarnings'">f.total_earnings</when>
<when test="sortField == 'createTime'">f.create_time</when>
<otherwise>f.create_time</otherwise>
</choose>
<choose>
<when test="order != null and order.toUpperCase() == 'DESC'">DESC</when>
<otherwise>ASC</otherwise>
</choose>
</when>
<otherwise>
ORDER BY f.create_time ASC
</otherwise>
</choose>
LIMIT ${size} OFFSET ${offset} LIMIT ${size} OFFSET ${offset}
</select> </select>

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ai.da.mapper.primary.ReferralMapper">
<!-- resources/mapper/ReferralMapper.xml -->
<select id="selectReferralWithAffiliate" resultType="com.ai.da.model.vo.ReferralPageQueryVO">
SELECT
r.*,
ac.user_name AS affiliateName
FROM
t_referral r
LEFT JOIN
t_account ac ON r.affiliate_account_id = ac.id
${ew.customSqlSegment} <!-- 动态条件 -->
</select>
<!-- Mapper.xml -->
<update id="batchDeleteByIds">
UPDATE t_referral
SET is_deleted = 1
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<!-- Mapper.xml -->
<select id="sumAmount" resultType="java.math.BigDecimal">
SELECT SUM(commission)
FROM t_referral
WHERE affiliate_id = #{affiliateId}
<if test="statusList != null and statusList.size() > 0">
AND status IN
<foreach collection="statusList" item="status" open="(" separator="," close=")">
#{status}
</foreach>
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
<if test="endTime != null">
AND create_time <![CDATA[<=]]> #{endTime}
</if>
</select>
<select id="getPersonalMonthlyIncome" resultType="java.util.Map">
SELECT
DATE_FORMAT(create_time, '%m') as yearMonth,
SUM(commission) AS totalCommission
FROM
t_referral
WHERE
YEAR(create_time) = #{year}
and affiliate_account_id = #{affiliateAccountId}
and is_deleted = 0
and status in ("Paid", "Unpaid")
GROUP BY
yearMonth
ORDER BY
yearMonth
</select>
<select id="getMonthlyAffiliateIncome" resultType="java.util.Map">
SELECT
DATE_FORMAT(create_time, '%m') as yearMonth,
SUM(amount) AS totalAmount,
SUM(CASE WHEN status = 'Paid' THEN commission ELSE 0 END) AS paidCommission,
SUM(CASE WHEN status = 'Unpaid' THEN commission ELSE 0 END) AS unpaidCommission
FROM
t_referral
WHERE
YEAR(create_time) = #{year}
AND MONTH(create_time) = #{month}
AND is_deleted = 0
AND status IN ('Paid', 'Unpaid')
GROUP BY
yearMonth
ORDER BY
yearMonth
</select>
</mapper>