diff --git a/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerCapitalUpdateEnum.java b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerCapitalUpdateEnum.java new file mode 100644 index 0000000..304ad31 --- /dev/null +++ b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerCapitalUpdateEnum.java @@ -0,0 +1,38 @@ +package com.gxwebsoft.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户分销钱包交易业务分类 + * @author xm + */ +@AllArgsConstructor +@Getter +public enum ShopDealerCapitalUpdateEnum { + + DISTRIBUTION_INCOME(10, "分销收入", true), + MANAGEMENT_INCOME(11, "团队管理津贴收入", true), + DIVIDEND_INCOME(12, "分红收入", true), + PROMOTION_INCOME(13, "现场推广收入", true), + WITHDRAW_PAYMENT(20, "提现支出", false), + TRANSFER_PAYMENT(30, "转账支出", false), + TRANSFER_INCOME(40, "转账收入", true), + FREEZE_MONEY_THAW(50, "佣金解冻", true), + DELIVERY_INCOME(60, "配送奖励", true), + ORDER_REFUND(70, "佣金退回(退单)", false), + ; + + /** + * 业务分类 + */ + private final Integer type; + /** + * 说明 + */ + private final String description; + /** + * 是否为增加余额 + */ + private final boolean add; +} diff --git a/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerTypeEnum.java b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerTypeEnum.java new file mode 100644 index 0000000..51cb3be --- /dev/null +++ b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerTypeEnum.java @@ -0,0 +1,28 @@ +package com.gxwebsoft.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 订单状态枚举 + */ +@AllArgsConstructor +@Getter +public enum ShopDealerTypeEnum { + + + FREEZE_ACCOUNT(1, "操作冻结账户余额"), + WITHDRAW_ACCOUNT(2, "操作提现账户余额【直接结算】"), + DEFROST(3, "解冻"); + + private final Integer code; + private final String desc; + + public Integer getCode() { + return code; + } + + public String getDesc() { + return desc; + } +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java b/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java index 53b114d..99a32c6 100644 --- a/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java +++ b/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java @@ -49,6 +49,9 @@ public interface UserMapper extends BaseMapper { @InterceptorIgnore(tenantLine = "true") List getOne(@Param("param") UserParam param); + @InterceptorIgnore(tenantLine = "true") + User getById(@Param("userId") Integer userId); + List selectListStatisticsRel(@Param("param") UserParam param); @InterceptorIgnore(tenantLine = "true") diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml b/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml index c16fae8..4196c93 100644 --- a/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml +++ b/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml @@ -260,5 +260,8 @@ WHERE a.user_id = #{userId} AND a.deleted = 0 + diff --git a/src/main/java/com/gxwebsoft/common/system/redis/OrderNoUtils.java b/src/main/java/com/gxwebsoft/common/system/redis/OrderNoUtils.java new file mode 100644 index 0000000..887f1ca --- /dev/null +++ b/src/main/java/com/gxwebsoft/common/system/redis/OrderNoUtils.java @@ -0,0 +1,38 @@ +package com.gxwebsoft.common.system.redis; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import javax.annotation.Resource; +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 支付序号的 Redis DAO + * + * @author 芋道源码 + */ +@Repository +public class OrderNoUtils { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 生成序号 + * @param prefix 前缀 + * @return 序号 + */ + public String generate(String prefix) { + // 递增序号 + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); + String key = noPrefix; + Long no = stringRedisTemplate.opsForValue().increment(key); + // 设置过期时间 + stringRedisTemplate.expire(key, Duration.ofMinutes(1L)); + return noPrefix + no; + } + +} diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java index 9d5d469..9cecbd2 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java @@ -4,18 +4,18 @@ import cn.afterturn.easypoi.excel.ExcelImportUtil; import cn.afterturn.easypoi.excel.entity.ImportParams; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.gxwebsoft.common.core.utils.JSONUtil; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.shop.service.ShopDealerUserService; -import com.gxwebsoft.shop.entity.ShopDealerUser; -import com.gxwebsoft.shop.param.ShopDealerUserParam; -import com.gxwebsoft.shop.param.ShopDealerUserImportParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.core.annotation.OperationLog; +import com.gxwebsoft.common.core.utils.JSONUtil; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.common.core.web.BatchParam; +import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto; +import com.gxwebsoft.shop.entity.ShopDealerUser; +import com.gxwebsoft.shop.param.ShopDealerUserImportParam; +import com.gxwebsoft.shop.param.ShopDealerUserParam; +import com.gxwebsoft.shop.service.ShopDealerUserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; @@ -178,4 +178,11 @@ public class ShopDealerUserController extends BaseController { return fail("导入失败", null); } + @Operation(summary = "分销结算") + @PostMapping("/dealerCapital") + @Deprecated + public ApiResult dealerCapital(@RequestBody ShopDealerUserReduceDto reduceDto) { + return success(shopDealerUserService.reduceBalance(reduceDto)); + } + } diff --git a/src/main/java/com/gxwebsoft/shop/dto/ShopDealerUserReduceDto.java b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerUserReduceDto.java new file mode 100644 index 0000000..cd9b53b --- /dev/null +++ b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerUserReduceDto.java @@ -0,0 +1,42 @@ +package com.gxwebsoft.shop.dto; + +import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum; +import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; + +/** + * 更新分销用户资金 + * @author xm + * @since 2026-05-06 + */ +@Data +@Schema(name = "ShopDealerUserReduceDto", description = "更新分销用户资金请求") +public class ShopDealerUserReduceDto { + + @Schema(description = "变动类型 1-操作冻结账户余额 2-操作提现账户余额【直接结算】 3-解冻") + @NotNull(message = "变更类型不可为空!") + private ShopDealerTypeEnum typeEnum; + + @Schema(description = "用户ID") + private Integer userId; + + @Schema(description = "订单主体用户ID") + private Integer orderUserId; + + @Schema(description = "订单号") + @NotEmpty(message = "订单号不能为空!") + private String orderNo; + + @Schema(description = "变更金额") + private BigDecimal price; + + @Schema(description = "业务类型") + @NotEmpty(message = "业务类型不能为空!") + private ShopDealerCapitalUpdateEnum updateEnum; + +} diff --git a/src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java b/src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java index b4d7101..99630eb 100644 --- a/src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java +++ b/src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java @@ -19,7 +19,7 @@ public class VerifyShopOrderDto { @NotBlank(message = "核销码不能为空") private String verifyCode; - @Schema(description = "核销码类型 1-普通核销【无推广佣金】 2-推广结算【有订单佣金】") + @Schema(description = "核销码类型 1-普通核销【无推广佣金】 2-推广结算【有订单佣金】", example = "2") private Integer verifyType; } diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java index 0dadc24..864b842 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java @@ -1,10 +1,11 @@ package com.gxwebsoft.shop.entity; import java.math.BigDecimal; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; + +import com.baomidou.mybatisplus.annotation.*; + import java.time.LocalDateTime; + import com.fasterxml.jackson.annotation.JsonFormat; import java.io.Serializable; import io.swagger.v3.oas.annotations.media.Schema; @@ -20,6 +21,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = false) @Schema(name = "ShopDealerCapital对象", description = "分销商资金明细表") +@TableName("shop_dealer_capital") public class ShopDealerCapital implements Serializable { private static final long serialVersionUID = 1L; @@ -27,26 +29,41 @@ public class ShopDealerCapital implements Serializable { @TableId(value = "id", type = IdType.AUTO) private Integer id; + @Schema(description = "订单编号") + private String no; + @Schema(description = "分销商用户ID") private Integer userId; + @Schema(description = "变动类型 1-操作冻结账户余额 2-操作提现账户余额【直接结算】 3-解冻") + private Integer type; + @Schema(description = "分销商昵称") @TableField(exist = false) private String nickName; - @Schema(description = "订单编号") + @Schema(description = "关联订单编号") private String orderNo; @Schema(description = "订单状态") @TableField(exist = false) private Integer orderStatus; - @Schema(description = "资金流动类型 (10佣金收入 20提现支出 30转账支出 40转账收入 50佣金解冻 60配送奖励)") + @Schema(description = "资金流动类型 (10佣金收入 20提现支出 30转账支出 40转账收入 50佣金解冻 60配送奖励 70佣金退回【退单】)") private Integer flowType; - @Schema(description = "金额") + @Schema(description = "变更金额") private BigDecimal money; + @Schema(description = "变更后金额") + private BigDecimal moneyAfter; + + @Schema(description = "变更冻结金额") + private BigDecimal freezeMoney; + + @Schema(description = "变更冻结后金额") + private BigDecimal freezeMoneyAfter; + @Schema(description = "描述") private String comments; @@ -63,6 +80,9 @@ public class ShopDealerCapital implements Serializable { @Schema(description = "商城ID") private Integer tenantId; + @Schema(description = "创建人") + private Integer creator; + @Schema(description = "创建时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; @@ -71,4 +91,8 @@ public class ShopDealerCapital implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; + @Schema(description = "是否删除 0-未删 1-已删") + @TableLogic + private Integer deleted; + } diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java index b067c3f..8db36da 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java @@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import java.io.Serializable; import io.swagger.v3.oas.annotations.media.Schema; @@ -20,6 +22,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = false) @Schema(name = "ShopDealerUser对象", description = "分销商用户记录表") +@TableName("shop_dealer_user") public class ShopDealerUser implements Serializable { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java b/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java index 51709cf..0b942d7 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java @@ -107,6 +107,9 @@ public class ShopGoods implements Serializable { @Schema(description = "配送奖金") private BigDecimal deliveryMoney; + @Schema(description = "推广核验佣金比率") + private BigDecimal verifyRate; + @Schema(description = "库存计算方式(10下单减库存 20付款减库存)") @JsonAlias({"cdeductStockType"}) private Integer deductStockType; diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java b/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java index 0c96f1d..586de72 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java @@ -331,6 +331,9 @@ public class ShopOrder implements Serializable { @Schema(description = "核销人") private Integer verifyUser; + @Schema(description = "推广核销佣金") + private BigDecimal verifyMoney; + @Schema(description = "修改时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; @@ -375,4 +378,8 @@ public class ShopOrder implements Serializable { @TableField(exist = false) private ShopOrderDelivery shopOrderDelivery; + + @Schema(description = "详细地址") + @TableField(exist = false) + private String fullAddress; } diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java b/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java index 490e45b..8ae5544 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import java.io.Serializable; import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; @@ -20,6 +22,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = false) @Schema(name = "ShopUserAddress对象", description = "收货地址") +@TableName("shop_user_address") public class ShopUserAddress implements Serializable { private static final long serialVersionUID = 1L; @@ -48,7 +51,7 @@ public class ShopUserAddress implements Serializable { @Schema(description = "收货地址") private String address; - @Schema(description = "收货地址") + @Schema(description = "详细地址") private String fullAddress; private String lat; diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java b/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java index 8e86728..ef2a6d5 100644 --- a/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java +++ b/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java @@ -2,6 +2,7 @@ package com.gxwebsoft.shop.service; import com.baomidou.mybatisplus.extension.service.IService; import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto; import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.shop.param.ShopDealerUserParam; @@ -42,4 +43,11 @@ public interface ShopDealerUserService extends IService { ShopDealerUser getByUserIdRel(Integer userId); boolean updateByUserId(ShopDealerUser shopDealerUser); + + /** + * 更新分销用户资金 + * @param reduceDto + * @return + */ + Boolean reduceBalance(ShopDealerUserReduceDto reduceDto); } diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopDealerUserServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopDealerUserServiceImpl.java index a5767d3..12fc4e3 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopDealerUserServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopDealerUserServiceImpl.java @@ -2,15 +2,31 @@ package com.gxwebsoft.shop.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.shop.mapper.ShopDealerUserMapper; -import com.gxwebsoft.shop.service.ShopDealerUserService; -import com.gxwebsoft.shop.entity.ShopDealerUser; -import com.gxwebsoft.shop.param.ShopDealerUserParam; +import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum; +import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum; +import com.gxwebsoft.common.core.utils.LoginUserUtil; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.common.system.mapper.UserMapper; +import com.gxwebsoft.common.system.redis.OrderNoUtils; +import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto; +import com.gxwebsoft.shop.entity.ShopDealerCapital; +import com.gxwebsoft.shop.entity.ShopDealerUser; +import com.gxwebsoft.shop.mapper.ShopDealerCapitalMapper; +import com.gxwebsoft.shop.mapper.ShopDealerUserMapper; +import com.gxwebsoft.shop.param.ShopDealerUserParam; +import com.gxwebsoft.shop.service.ShopDealerUserService; +import lombok.AllArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * 分销商用户记录表Service实现 @@ -19,8 +35,17 @@ import java.util.List; * @since 2025-08-11 23:51:41 */ @Service +@AllArgsConstructor public class ShopDealerUserServiceImpl extends ServiceImpl implements ShopDealerUserService { + private UserMapper userMapper; + + private OrderNoUtils orderNoUtils; + + private ShopDealerCapitalMapper shopDealerCapitalMapper; + + private static final int TENANT_ID = 10584; + @Override public PageResult pageRel(ShopDealerUserParam param) { PageParam page = new PageParam<>(param); @@ -58,4 +83,196 @@ public class ShopDealerUserServiceImpl extends ServiceImpl 0; } + @Override + @Transactional + public Boolean reduceBalance(ShopDealerUserReduceDto entity) { + //1.获取入参 + log.error("分销账户资金变更明细:" + entity); + ShopDealerTypeEnum typeEnum = entity.getTypeEnum(); + ShopDealerCapitalUpdateEnum updateEnum = entity.getUpdateEnum(); + + //2.变动类型为 1-操作冻结账户余额 2-操作提现账户余额【直接结算】是需判断金额值问题 + if(ShopDealerTypeEnum.FREEZE_ACCOUNT.equals(typeEnum) || ShopDealerTypeEnum.WITHDRAW_ACCOUNT.equals(typeEnum)){ + //查询分销用户信息 + ShopDealerUser dealerUser = getDealerUser(entity.getUserId()); + BigDecimal price = entity.getPrice(); + + switch (updateEnum){ + case DISTRIBUTION_INCOME: + case MANAGEMENT_INCOME: + case DIVIDEND_INCOME: + case PROMOTION_INCOME: + case TRANSFER_INCOME: + case DELIVERY_INCOME:{ + break; + } + case WITHDRAW_PAYMENT: + case TRANSFER_PAYMENT: + case ORDER_REFUND:{ + price.negate(); + break; + } + default:{ + throw new RuntimeException("暂不支持此类型!"); + } + } + + String no = orderNoUtils.generate("C"); + //3.操作冻结账户余额 + if(ShopDealerTypeEnum.FREEZE_ACCOUNT.equals(typeEnum)){ //操作冻结账户余额 + //3.1 账户变更冻结金额 + dealerUser.setFreezeMoney(dealerUser.getFreezeMoney().add(price)); + dealerUser.setTotalMoney(dealerUser.getTotalMoney().add(price)); + baseMapper.updateById(dealerUser); + + //3.2 生成流水 + ShopDealerCapital dealerCapital = new ShopDealerCapital(); + dealerCapital.setNo(no); + dealerCapital.setUserId(entity.getUserId()); + dealerCapital.setType(typeEnum.getCode()); + dealerCapital.setOrderNo(entity.getOrderNo()); + dealerCapital.setFlowType(updateEnum.getType()); + dealerCapital.setFreezeMoney(price); + dealerCapital.setFreezeMoneyAfter(dealerUser.getFreezeMoney()); + dealerCapital.setMoneyAfter(dealerUser.getMoney()); + dealerCapital.setComments(updateEnum.getDescription()); + dealerCapital.setToUserId(entity.getOrderUserId()); + dealerCapital.setTenantId(TENANT_ID); + dealerCapital.setCreateTime(LocalDateTime.now()); + User loginUser = LoginUserUtil.getLoginUser(); + if(loginUser != null){ + dealerCapital.setCreator(loginUser.getUserId()); + } + shopDealerCapitalMapper.insert(dealerCapital); + } + + //4.操作提现账户余额【直接结算】 + else if(ShopDealerTypeEnum.WITHDRAW_ACCOUNT.equals(typeEnum)){ + //4.1 账户变更冻结金额 + dealerUser.setMoney(dealerUser.getMoney().add(price)); + dealerUser.setTotalMoney(dealerUser.getTotalMoney().add(price)); + baseMapper.updateById(dealerUser); + + + //4.2 生成流水 + ShopDealerCapital dealerCapital = new ShopDealerCapital(); + dealerCapital.setNo(no); + dealerCapital.setUserId(entity.getUserId()); + dealerCapital.setType(typeEnum.getCode()); + dealerCapital.setOrderNo(entity.getOrderNo()); + dealerCapital.setFlowType(updateEnum.getType()); + dealerCapital.setMoney(price); + dealerCapital.setMoneyAfter(dealerUser.getMoney()); + dealerCapital.setFreezeMoneyAfter(dealerUser.getFreezeMoney()); + dealerCapital.setComments(updateEnum.getDescription()); + dealerCapital.setToUserId(entity.getOrderUserId()); + dealerCapital.setTenantId(TENANT_ID); + dealerCapital.setCreateTime(LocalDateTime.now()); + User loginUser = LoginUserUtil.getLoginUser(); + if(loginUser != null){ + dealerCapital.setCreator(loginUser.getUserId()); + } + shopDealerCapitalMapper.insert(dealerCapital); + } + } + + //5.操作解冻业务【按订单号结算,可能一个订单号存在2笔待解冻金额】 + else if(ShopDealerTypeEnum.DEFROST.equals(typeEnum)){ + //5.1 判断是否为指定的解冻业务 + if(!ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW.equals(entity.getUpdateEnum())){ + throw new RuntimeException("暂不支持此类型!"); + } + + //5.2 查询订单结算流水 + LambdaQueryWrapper capitalLambdaQueryWrapper = new LambdaQueryWrapper().eq(ShopDealerCapital::getOrderNo, entity.getOrderNo()); + List dealerCapitalList = shopDealerCapitalMapper.selectList(capitalLambdaQueryWrapper); + + //5.3 根据用户遍历查询及执行解冻业务 + if(CollectionUtils.isNotEmpty(dealerCapitalList)){ + List userIdList = dealerCapitalList.stream().map(ShopDealerCapital::getUserId).distinct().collect(Collectors.toList()); + List shopDealerUserList = lambdaQuery().in(ShopDealerUser::getUserId, userIdList).list(); + + Map> userCapitalMap = dealerCapitalList.stream().collect(Collectors.groupingBy(ShopDealerCapital::getUserId)); + userCapitalMap.forEach((k, value) ->{ + + //5.4 查询是否存在待解冻订单数据 + ShopDealerCapital userCapital = value.stream().filter(capital -> ShopDealerTypeEnum.FREEZE_ACCOUNT.getCode().equals(capital.getType())).findFirst().orElse(null); + if(userCapital != null){ + + //5.5 查询此订单是否已结算 + ShopDealerCapital alreadyCapital = value.stream().filter(capital -> ShopDealerTypeEnum.DEFROST.getCode().equals(capital.getType())).findFirst().orElse(null); + if(alreadyCapital == null){ + ShopDealerUser dealerUser = shopDealerUserList.stream().filter(shopDealerUser -> k.equals(shopDealerUser.getUserId())).findFirst().orElse(null); + + //5.6 判断分销账户分销金额是否大于解冻金额 + BigDecimal freezeMoney = userCapital.getFreezeMoney(); + if(dealerUser != null && dealerUser.getFreezeMoney().compareTo(freezeMoney) >= 0){ + //5.7 账户解冻金额、可提现增加金额 + dealerUser.setFreezeMoney(dealerUser.getFreezeMoney().subtract(freezeMoney)); + dealerUser.setMoney(dealerUser.getMoney().add(freezeMoney)); + baseMapper.updateById(dealerUser); + + //5.8 生成流水 + ShopDealerCapital dealerCapital = new ShopDealerCapital(); + String no = orderNoUtils.generate("C"); + dealerCapital.setNo(no); + dealerCapital.setUserId(k); + dealerCapital.setType(typeEnum.getCode()); + dealerCapital.setOrderNo(entity.getOrderNo()); + dealerCapital.setFlowType(updateEnum.getType()); + dealerCapital.setFreezeMoney(freezeMoney.negate()); + dealerCapital.setFreezeMoneyAfter(dealerUser.getFreezeMoney()); + dealerCapital.setMoney(freezeMoney); + dealerCapital.setMoneyAfter(dealerUser.getMoney()); + dealerCapital.setComments(updateEnum.getDescription()); + dealerCapital.setToUserId(entity.getOrderUserId()); + dealerCapital.setTenantId(TENANT_ID); + dealerCapital.setCreateTime(LocalDateTime.now()); + User loginUser = LoginUserUtil.getLoginUser(); + if (loginUser != null) { + dealerCapital.setCreator(loginUser.getUserId()); + } + shopDealerCapitalMapper.insert(dealerCapital); + } + } + } + }); + }else { + log.error("未查询到待解冻订单信息:" + entity.getOrderNo()); + } + }else { + throw new RuntimeException("暂不支持此变动类型业务!"); + } + return Boolean.TRUE; + } + + public ShopDealerUser getDealerUser(Integer userId){ + ShopDealerUser shopDealerUser = baseMapper.selectOne(new LambdaQueryWrapper().eq(ShopDealerUser::getUserId, userId)); + if(shopDealerUser == null){ + User user = userMapper.getById(userId); + if(user == null){ + throw new RuntimeException("用户信息不存在!"); + } + + ShopDealerUser dealerUser = new ShopDealerUser(); + dealerUser.setType(0); + dealerUser.setUserId(userId); + dealerUser.setAvatar(user.getAvatar()); + dealerUser.setRealName(user.getRealName()); + dealerUser.setMobile(user.getMobile()); + dealerUser.setMoney(BigDecimal.ZERO); + dealerUser.setFreezeMoney(BigDecimal.ZERO); + dealerUser.setTotalMoney(BigDecimal.ZERO); + dealerUser.setTenantId(TENANT_ID); + dealerUser.setCreateTime(LocalDateTime.now()); + dealerUser.setDealerLevel(0); + baseMapper.insert(dealerUser); + + shopDealerUser = dealerUser; + } + return shopDealerUser; + } + + + } diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java index 06a1b3a..ba64a85 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java @@ -7,12 +7,21 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gxwebsoft.common.core.context.TenantContext; import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.config.CertificateProperties; +import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum; +import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum; +import com.gxwebsoft.common.core.exception.enums.GlobalErrorCodeConstants; import com.gxwebsoft.common.core.utils.*; import com.gxwebsoft.common.core.service.PaymentCacheService; import com.gxwebsoft.common.core.service.EnvironmentAwarePaymentService; import com.gxwebsoft.common.core.config.SpringContextUtil; import com.gxwebsoft.common.system.entity.Payment; +import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.common.system.mapper.UserMapper; +import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto; +import com.gxwebsoft.shop.dto.VerifyShopOrderDto; import com.gxwebsoft.shop.entity.*; +import com.gxwebsoft.shop.mapper.ShopGoodsMapper; +import com.gxwebsoft.shop.mapper.ShopUserAddressMapper; import com.gxwebsoft.shop.service.*; import com.wechat.pay.java.core.RSAAutoCertificateConfig; import lombok.extern.slf4j.Slf4j; @@ -35,6 +44,7 @@ import com.wechat.pay.java.service.payments.model.Transaction; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; @@ -44,6 +54,7 @@ import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** @@ -91,6 +102,14 @@ public class ShopOrderServiceImpl extends ServiceImpl().eq(ShopOrder::getVerifyCode, verifyDto.getVerifyCode())); + if(shopOrder == null){ + throw new RuntimeException(GlobalErrorCodeConstants.NOT_FOUND.getMsg()); + } + + //4.判断当前订单状态是否允许核销 + if(Arrays.asList(2,3,4,6,7).contains(shopOrder.getOrderStatus())){ + throw new RuntimeException("订单当前状态不可核销!"); + } + + //5.判断订单是否已支付 + if(!shopOrder.getPayStatus()){ + throw new RuntimeException("订单未支付,不可核销!"); + } + + //6.判断是否已核销 + if(shopOrder.getVerifyStatus() != null && shopOrder.getVerifyStatus() == 1){ + throw new RuntimeException("此订单已核销,无需重复核销!"); + } + + shopOrder.setVerifyStatus(1); + shopOrder.setVerifyTime(LocalDateTime.now()); + shopOrder.setVerifyUser(loginUser.getUserId()); + + //7.只有推广结算才会记佣且判断是否在设定有效期内核销订单【是:计算订单核销佣金 否:不计算核销佣金】 + if(verifyType == 2 && shopOrder.getVerifyExpTime().isAfter(LocalDateTime.now())){ + List orderGoodsList = shopOrderGoodsService.getListByOrderId(shopOrder.getOrderId()); + if(!CollectionUtils.isEmpty(orderGoodsList)){ + List goodsIdList = orderGoodsList.stream().map(ShopOrderGoods::getGoodsId).distinct().collect(Collectors.toList()); + List shopGoods = shopGoodsMapper.selectBatchIds(goodsIdList); + + //计算推广佣金 + AtomicReference verifyMoney = new AtomicReference<>(BigDecimal.ZERO); + + orderGoodsList.forEach(orderGoods -> { + ShopGoods shopGood = shopGoods.stream().filter(goods -> orderGoods.getGoodsId().equals(goods.getGoodsId()) && + goods.getVerifyRate().compareTo(BigDecimal.ZERO) > 0).findFirst().orElse(null); + + if (shopGood != null) { + // 计算单商品核价金额 + BigDecimal money = orderGoods.getPrice() + .multiply(BigDecimal.valueOf(orderGoods.getTotalNum())) + .multiply(shopGood.getVerifyRate()) + .divide(BigDecimal.valueOf(100), 3, RoundingMode.HALF_UP); // 除100,保留2位小数 + + // ✅ 原子更新(关键!) + verifyMoney.set(verifyMoney.get().add(money)); + } + }); + shopOrder.setVerifyMoney(verifyMoney.get()); + + //结算推广佣金 + if(verifyMoney.get().compareTo(BigDecimal.ZERO) > 0){ + ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto(); + reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT); + reduceDto.setUserId(loginUser.getUserId()); + reduceDto.setOrderUserId(shopOrder.getUserId()); + reduceDto.setOrderNo(shopOrder.getOrderNo()); + reduceDto.setPrice(verifyMoney.get()); + reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.PROMOTION_INCOME); + + Boolean reduceBalance = shopDealerUserService.reduceBalance(reduceDto); + if(!reduceBalance){ + log.error("推广核销失败,结算推广佣金失败:" + reduceDto); + } + + } + } + } + + //8.判断是否自提订单,如是自提订单需调整为订单完成,用户已收货状态 + if(shopOrder.getOrderType() != null && shopOrder.getOrderType() == 2){ + shopOrder.setOrderStatus(1); + shopOrder.setDeliveryStatus(20); + + //分销佣金结算【团队长管理津贴、股东分红在订单完成后再结算】 + ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto(); + reduceDto.setTypeEnum(ShopDealerTypeEnum.DEFROST); + reduceDto.setOrderUserId(shopOrder.getUserId()); + reduceDto.setOrderNo(shopOrder.getOrderNo()); + reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW); + + Boolean reduceBalance = shopDealerUserService.reduceBalance(reduceDto); + if(!reduceBalance){ + log.error("分销核销失败,分销结算失败:" + reduceDto); + } + } + return baseMapper.updateById(shopOrder) > 0; + }catch (Exception e){ + throw new RuntimeException("核销失败:" + e.getMessage()); + } + } + private static Long getLong(Map raw, String key) { Object value = raw.get(key); if (value == null) {