|
@@ -0,0 +1,226 @@
|
|
|
|
|
+package com.etotem.cfc.service;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.etotem.cfc.entity.ButlerCommissionSettlement;
|
|
|
|
|
+import com.etotem.cfc.entity.ButlerProfile;
|
|
|
|
|
+import com.etotem.cfc.entity.ButlerServiceRecord;
|
|
|
|
|
+import com.etotem.cfc.entity.SysConfig;
|
|
|
|
|
+import com.etotem.cfc.mapper.ButlerCommissionSettlementMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.ButlerProfileMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.ButlerServiceRecordMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.SysConfigMapper;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import java.time.LocalDate;
|
|
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+@Service
|
|
|
|
|
+public class ButlerCommissionService {
|
|
|
|
|
+
|
|
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(ButlerCommissionService.class);
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ButlerServiceRecordMapper serviceRecordMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ButlerCommissionSettlementMapper settlementMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ButlerProfileMapper butlerProfileMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private SysConfigMapper sysConfigMapper;
|
|
|
|
|
+
|
|
|
|
|
+ // 默认配置(基点为10000=100%)
|
|
|
|
|
+ private static final int DEFAULT_PLATFORM_RATE = 2000; // 平台服务费率 20%
|
|
|
|
|
+ private static final int DEFAULT_ANNUAL_L1_RATE = 1000; // L1年费管家分润率 10%
|
|
|
|
|
+ private static final int DEFAULT_ANNUAL_L2_RATE = 5000; // L2年费管家分润率 50%
|
|
|
|
|
+ private static final int DEFAULT_PER_SESSION_PLATFORM_RATE = 2000; // 按次平台费率 20%
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建按次服务记录
|
|
|
|
|
+ * @param butlerId 管家ID
|
|
|
|
|
+ * @param memberUserId 会员ID
|
|
|
|
|
+ * @param amount 服务金额(P点)
|
|
|
|
|
+ * @param remark 备注
|
|
|
|
|
+ * @return 创建的服务记录
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public ButlerServiceRecord createServiceRecord(Long butlerId, Long memberUserId, Long amount, String remark) {
|
|
|
|
|
+ int platformRate = getSysConfigInt("butler_per_session_platform_rate", DEFAULT_PER_SESSION_PLATFORM_RATE);
|
|
|
|
|
+ long platformFee = amount * platformRate / 10000;
|
|
|
|
|
+ long butlerAmount = amount - platformFee;
|
|
|
|
|
+
|
|
|
|
|
+ ButlerServiceRecord record = new ButlerServiceRecord();
|
|
|
|
|
+ record.setButlerId(butlerId);
|
|
|
|
|
+ record.setMemberUserId(memberUserId);
|
|
|
|
|
+ record.setServiceType("per_session");
|
|
|
|
|
+ record.setAmountP(amount);
|
|
|
|
|
+ record.setPlatformFeeP(platformFee);
|
|
|
|
|
+ record.setButlerAmountP(butlerAmount);
|
|
|
|
|
+ record.setYearMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
|
|
|
|
|
+ record.setRemark(remark);
|
|
|
|
|
+ record.setCreatedAt(new Date());
|
|
|
|
|
+ serviceRecordMapper.insert(record);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("Created per-session service record: butlerId={}, amount={}, platformFee={}, butlerAmount={}",
|
|
|
|
|
+ butlerId, amount, platformFee, butlerAmount);
|
|
|
|
|
+ return record;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算指定管家某月的结算
|
|
|
|
|
+ * @param butlerId 管家ID
|
|
|
|
|
+ * @param yearMonth YYYY-MM格式
|
|
|
|
|
+ * @return 结算单ID
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public Long calculateButlerSettlement(Long butlerId, String yearMonth) {
|
|
|
|
|
+ // 查询管家当月所有服务记录
|
|
|
|
|
+ List<ButlerServiceRecord> records = serviceRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerServiceRecord>()
|
|
|
|
|
+ .eq(ButlerServiceRecord::getButlerId, butlerId)
|
|
|
|
|
+ .eq(ButlerServiceRecord::getYearMonth, yearMonth)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (records.isEmpty()) {
|
|
|
|
|
+ log.info("No service records for butlerId={} in {}", butlerId, yearMonth);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 统计各项
|
|
|
|
|
+ long annualBaseAmount = 0;
|
|
|
|
|
+ long annualButlerAmount = 0;
|
|
|
|
|
+ long annualPlatformAmount = 0;
|
|
|
|
|
+ long perSessionTotal = 0;
|
|
|
|
|
+ long perSessionPlatformTotal = 0;
|
|
|
|
|
+ long perSessionButlerTotal = 0;
|
|
|
|
|
+ long totalButlerAmount = 0;
|
|
|
|
|
+ int memberCount = 0;
|
|
|
|
|
+
|
|
|
|
|
+ for (ButlerServiceRecord r : records) {
|
|
|
|
|
+ if ("annual".equals(r.getServiceType())) {
|
|
|
|
|
+ annualBaseAmount += r.getAmountP() != null ? r.getAmountP() : 0;
|
|
|
|
|
+ annualButlerAmount += r.getButlerAmountP() != null ? r.getButlerAmountP() : 0;
|
|
|
|
|
+ annualPlatformAmount += r.getPlatformFeeP() != null ? r.getPlatformFeeP() : 0;
|
|
|
|
|
+ } else if ("per_session".equals(r.getServiceType())) {
|
|
|
|
|
+ perSessionTotal += r.getAmountP() != null ? r.getAmountP() : 0;
|
|
|
|
|
+ perSessionPlatformTotal += r.getPlatformFeeP() != null ? r.getPlatformFeeP() : 0;
|
|
|
|
|
+ perSessionButlerTotal += r.getButlerAmountP() != null ? r.getButlerAmountP() : 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ totalButlerAmount = annualButlerAmount + perSessionButlerTotal;
|
|
|
|
|
+
|
|
|
|
|
+ ButlerProfile profile = butlerProfileMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerProfile>().eq(ButlerProfile::getUserId, butlerId));
|
|
|
|
|
+ if (profile != null) {
|
|
|
|
|
+ memberCount = profile.getMemberCount() != null ? profile.getMemberCount() : 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建或更新结算单
|
|
|
|
|
+ ButlerCommissionSettlement existing = settlementMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerCommissionSettlement>()
|
|
|
|
|
+ .eq(ButlerCommissionSettlement::getButlerId, butlerId)
|
|
|
|
|
+ .eq(ButlerCommissionSettlement::getYearMonth, yearMonth)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (existing != null) {
|
|
|
|
|
+ existing.setMemberCount(memberCount);
|
|
|
|
|
+ existing.setAnnualBaseAmountP(annualBaseAmount);
|
|
|
|
|
+ existing.setAnnualButlerAmountP(annualButlerAmount);
|
|
|
|
|
+ existing.setAnnualPlatformAmountP(annualPlatformAmount);
|
|
|
|
|
+ existing.setPerSessionTotalP(perSessionTotal);
|
|
|
|
|
+ existing.setPerSessionPlatformTotalP(perSessionPlatformTotal);
|
|
|
|
|
+ existing.setPerSessionButlerTotalP(perSessionButlerTotal);
|
|
|
|
|
+ existing.setTotalButlerAmountP(totalButlerAmount);
|
|
|
|
|
+ settlementMapper.updateById(existing);
|
|
|
|
|
+ log.info("Updated settlement id={} for butlerId={}, month={}, totalButlerAmount={}",
|
|
|
|
|
+ existing.getId(), butlerId, yearMonth, totalButlerAmount);
|
|
|
|
|
+ return existing.getId();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ButlerCommissionSettlement settlement = new ButlerCommissionSettlement();
|
|
|
|
|
+ settlement.setButlerId(butlerId);
|
|
|
|
|
+ settlement.setYearMonth(yearMonth);
|
|
|
|
|
+ settlement.setMemberCount(memberCount);
|
|
|
|
|
+ settlement.setAnnualBaseAmountP(annualBaseAmount);
|
|
|
|
|
+ settlement.setAnnualButlerAmountP(annualButlerAmount);
|
|
|
|
|
+ settlement.setAnnualPlatformAmountP(annualPlatformAmount);
|
|
|
|
|
+ settlement.setPerSessionTotalP(perSessionTotal);
|
|
|
|
|
+ settlement.setPerSessionPlatformTotalP(perSessionPlatformTotal);
|
|
|
|
|
+ settlement.setPerSessionButlerTotalP(perSessionButlerTotal);
|
|
|
|
|
+ settlement.setTotalButlerAmountP(totalButlerAmount);
|
|
|
|
|
+ settlement.setStatus("pending");
|
|
|
|
|
+ settlement.setCreatedAt(new Date());
|
|
|
|
|
+ settlementMapper.insert(settlement);
|
|
|
|
|
+ log.info("Created settlement id={} for butlerId={}, month={}, totalButlerAmount={}",
|
|
|
|
|
+ settlement.getId(), butlerId, yearMonth, totalButlerAmount);
|
|
|
|
|
+ return settlement.getId();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 批量计算所有管家的月度结算
|
|
|
|
|
+ * @param yearMonth YYYY-MM格式,null表示上月
|
|
|
|
|
+ * @return 处理的管家数
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public int batchCalculateMonthlySettlement(String yearMonth) {
|
|
|
|
|
+ if (yearMonth == null) {
|
|
|
|
|
+ LocalDate lastMonth = LocalDate.now().minusMonths(1);
|
|
|
|
|
+ yearMonth = lastMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询所有审核通过的管家
|
|
|
|
|
+ List<ButlerProfile> butlers = butlerProfileMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerProfile>()
|
|
|
|
|
+ .eq(ButlerProfile::getStatus, "approved")
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ for (ButlerProfile butler : butlers) {
|
|
|
|
|
+ Long settlementId = calculateButlerSettlement(butler.getUserId(), yearMonth);
|
|
|
|
|
+ if (settlementId != null) {
|
|
|
|
|
+ count++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("Batch settlement for {}: processed {} butlers", yearMonth, count);
|
|
|
|
|
+ return count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 标记结算单为已发放
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public void markSettlementPaid(Long settlementId, Long settledBy) {
|
|
|
|
|
+ ButlerCommissionSettlement settlement = settlementMapper.selectById(settlementId);
|
|
|
|
|
+ if (settlement == null) {
|
|
|
|
|
+ log.warn("Settlement not found: id={}", settlementId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ settlement.setStatus("paid");
|
|
|
|
|
+ settlement.setSettledAt(new Date());
|
|
|
|
|
+ settlement.setSettledBy(settledBy);
|
|
|
|
|
+ settlementMapper.updateById(settlement);
|
|
|
|
|
+ log.info("Marked settlement id={} as paid", settlementId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int getSysConfigInt(String key, int defaultValue) {
|
|
|
|
|
+ SysConfig config = sysConfigMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getConfigKey, key));
|
|
|
|
|
+ if (config != null && config.getConfigValue() != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return Integer.parseInt(config.getConfigValue());
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ log.warn("Invalid sys_config value for {}: {}", key, config.getConfigValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return defaultValue;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|