# yonghu/fadan_fenhong_utils.py # 罚款缴纳分红处理公共方法(支持更新罚单中的申请人分红金额) import logging from decimal import Decimal from django.db import transaction from orders.models import Penalty, PenaltyBonus, PenaltyBonusRate from users.models import UserShangjia logger = logging.getLogger(__name__) # 身份映射字典 IDENTITY_MAP = { 1: {'fenhong': 2, 'lilv': 1}, # 客服 2: {'fenhong': 5, 'lilv': 2}, # 售后 3: {'fenhong': 3, 'lilv': 3}, # 管理员 4: {'fenhong': 4, 'lilv': 4}, # 店铺 5: {'fenhong': 1, 'lilv': 5}, # 商家 } def process_fadan_fenhong(fadan_id): """ 罚款缴纳后的分红处理(在回调中调用) 参数: fadan_id: 罚单 ID 返回: (success, message) """ try: # 1. 获取罚单对象(必须是已缴纳状态) try: fadan = Penalty.objects.get(id=fadan_id, Status=2) except Penalty.DoesNotExist: logger.error(f"罚单 {fadan_id} 不存在或状态不是已缴纳") return False, "罚单不存在或状态异常" # 防重复分红(微信回调重试时不可二次加钱) if fadan.ApplicantBonusAmount and Decimal(str(fadan.ApplicantBonusAmount)) > 0: logger.info( f"罚单 {fadan_id} 已分红 {fadan.ApplicantBonusAmount} 元,跳过重复处理" ) return True, "已分红" # 2. 获取申请处罚者ID和身份(源身份数字) shenqing_chufa = fadan.ApplicantID src_shenfen = fadan.ApplicantIdentity if not shenqing_chufa: logger.info(f"罚单 {fadan_id} 无申请处罚者,不进行分红") # 即使无申请者,也可将分红金额字段设为0(默认已是0) return True, "无申请处罚者,无需分红" # 3. 映射身份数字 if src_shenfen not in IDENTITY_MAP: logger.error(f"未知的申请处罚者身份: {src_shenfen},罚单 {fadan_id}") return False, f"未知身份 {src_shenfen}" mapped = IDENTITY_MAP[src_shenfen] fenhong_shenfen = mapped['fenhong'] # 用于 FadanFenhong.shenfen lilv_shenfen = mapped['lilv'] # 用于 FadanFenhongLilv.shenfen # 4. 获取罚款金额 fakuan_jine = fadan.FineAmount if fakuan_jine <= 0: logger.info(f"罚单 {fadan_id} 罚款金额为0,不产生分红") fadan.ApplicantBonusAmount = Decimal('0.00') fadan.save(update_fields=['ApplicantBonusAmount']) return True, "罚款金额为0,无分红" # 5. 确定分红利率 rate = _get_fenhong_rate(shenqing_chufa, fenhong_shenfen, lilv_shenfen, club_id=fadan.ClubID) if rate is None: logger.warning(f"未找到身份 {src_shenfen} (映射后 fenhong_shenfen={fenhong_shenfen}, lilv_shenfen={lilv_shenfen}) 的分红利率,罚单 {fadan_id} 不进行分红") fadan.ApplicantBonusAmount = Decimal('0.00') fadan.save(update_fields=['ApplicantBonusAmount']) return True, "未配置分红利率,无分红" # 6. 计算分红金额 fenhong_jine = (Decimal(str(fakuan_jine)) * Decimal(str(rate))).quantize(Decimal('0.01')) if fenhong_jine <= 0: logger.info(f"罚单 {fadan_id} 计算分红金额为0,无需处理") fadan.ApplicantBonusAmount = Decimal('0.00') fadan.save(update_fields=['ApplicantBonusAmount']) return True, "分红金额为0" club_id = fadan.ClubID or 'xq' # 7. 根据申请处罚者身份执行不同的分红逻辑 # src_shenfen == 5 代表商家(源身份) if src_shenfen == 5: # 商家:分红统计累加 + 余额入账(可提现分红字段保持0,钱进商家余额) with transaction.atomic(): fadan = Penalty.objects.select_for_update().get(id=fadan_id, Status=2) if fadan.ApplicantBonusAmount and Decimal(str(fadan.ApplicantBonusAmount)) > 0: logger.info(f"罚单 {fadan_id} 事务内检测到已分红,跳过") return True, "已分红" if not UserShangjia.objects.filter(user__UserUID=shenqing_chufa).exists(): logger.error(f"商家 {shenqing_chufa} 扩展表不存在,无法分红入账") return False, "商家资料不存在,无法分红" fenhong_record, created = PenaltyBonus.objects.get_or_create( BeneficiaryID=shenqing_chufa, Identity=fenhong_shenfen, ClubID=club_id, defaults={ 'TotalAmount': Decimal('0.00'), 'WithdrawableAmount': Decimal('0.00'), 'BonusCount': 0, 'IndividualRate': rate, 'is_individual_rate': False } ) fenhong_record.TotalAmount += fenhong_jine # 可提现分红不累加,保持0 fenhong_record.BonusCount += 1 fenhong_record.save() # 直接增加商家余额(UserShangjia.yue) shangjia = UserShangjia.objects.select_for_update().get(user__UserUID=shenqing_chufa) shangjia.yue += fenhong_jine shangjia.save(update_fields=['yue']) logger.info(f"商家 {shenqing_chufa} 余额增加 {fenhong_jine} 元") # 更新罚单中的申请人分红金额 fadan.ApplicantBonusAmount = fenhong_jine fadan.save(update_fields=['ApplicantBonusAmount']) else: # 其他身份(客服、售后、管理员、店铺):累加可提现分红 with transaction.atomic(): fadan = Penalty.objects.select_for_update().get(id=fadan_id, Status=2) if fadan.ApplicantBonusAmount and Decimal(str(fadan.ApplicantBonusAmount)) > 0: logger.info(f"罚单 {fadan_id} 事务内检测到已分红,跳过") return True, "已分红" fenhong_record, created = PenaltyBonus.objects.get_or_create( BeneficiaryID=shenqing_chufa, Identity=fenhong_shenfen, ClubID=club_id, defaults={ 'TotalAmount': Decimal('0.00'), 'WithdrawableAmount': Decimal('0.00'), 'BonusCount': 0, 'IndividualRate': rate, 'is_individual_rate': False } ) fenhong_record.TotalAmount += fenhong_jine fenhong_record.WithdrawableAmount += fenhong_jine fenhong_record.BonusCount += 1 fenhong_record.save() # 更新罚单中的申请人分红金额 fadan.ApplicantBonusAmount = fenhong_jine fadan.save(update_fields=['ApplicantBonusAmount']) logger.info(f"罚单 {fadan_id} 分红处理成功: 申请者 {shenqing_chufa}, 分红金额 {fenhong_jine} 元, 源身份 {src_shenfen}, 映射后分红身份 {fenhong_shenfen}") return True, "分红处理成功" except Exception as e: logger.error(f"罚单 {fadan_id} 分红处理异常: {str(e)}", exc_info=True) return False, f"分红处理异常: {str(e)}" def _get_fenhong_rate(fenhongzhe_id, fenhong_shenfen, lilv_shenfen, club_id='xq'): """ 获取分红利率(优先使用个人单独配置,否则使用全局身份配置) """ try: individual = PenaltyBonus.objects.get( BeneficiaryID=fenhongzhe_id, Identity=fenhong_shenfen, ClubID=club_id, is_individual_rate=True ) return individual.IndividualRate except PenaltyBonus.DoesNotExist: pass try: global_rate = PenaltyBonusRate.objects.get(Identity=lilv_shenfen) return global_rate.Rate except PenaltyBonusRate.DoesNotExist: return None