Browse Source

Merge branch 'test-xq_tixian' into test-xq1

xuqiang 1 year ago
parent
commit
842ff9a1ee

+ 3 - 0
src/main/java/com/ydtech/modules/admin/constants/SwitchConfig.java

@@ -40,6 +40,9 @@ public enum SwitchConfig {
 
     CT_DISCOUNT_01("ctDiscountFlag", "诚泰自动调整折扣"),
 
+    VIRTUAL_WITHDRAWAL("virtualWithdrawal", "虚拟提现"),
+
+
     CUSTOMER("customer", "客服功能")
     ;
 

+ 4 - 0
src/main/java/com/ydtech/modules/admin/dao/SysUserMapper.java

@@ -576,6 +576,10 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
 
 
     Page<SysUserBaseVO> findPartnerUserList(@Param("page") Page<SysUserBaseVO> page, @Param("user") SysUserDto user);
+
+    @Select(" SELECT id FROM sys_user WHERE `status` = 1 and internal_id = 1 ")
+    List<String> queryInternalNumber();
+
 }
 
 

+ 4 - 0
src/main/java/com/ydtech/modules/admin/service/SysUserService.java

@@ -393,6 +393,10 @@ public interface SysUserService extends BaseIService<SysUser> {
     Page<SysUserBaseVO> findPartnerUserList(SysUserDto sysUserDto);
 
     HttpResult newSaveUserInfoByPartner(SysUser2Vo sysUserVo);
+
+    List<String> queryInternalNumber();
+
+
 }
 
 

+ 16 - 2
src/main/java/com/ydtech/modules/admin/service/impl/SysUserAccountServiceImpl.java

@@ -8,6 +8,7 @@ import com.ydtech.core.page.HttpResult;
 import com.ydtech.exception.SystemException;
 import com.ydtech.modules.admin.constants.ManagementSource;
 import com.ydtech.modules.admin.constants.MoneyAttrConstants;
+import com.ydtech.modules.admin.constants.SwitchConfig;
 import com.ydtech.modules.admin.constants.WithdrawTypeConstants;
 import com.ydtech.modules.admin.dao.SysUserAccountMapper;
 import com.ydtech.modules.admin.dao.SysUserBankCardMapper;
@@ -32,8 +33,13 @@ import com.ydtech.modules.order.entity.InsOrders;
 import com.ydtech.modules.order.service.InsAreaCompanyService;
 import com.ydtech.modules.protocols.entity.po.InsFeeOrderNew;
 import com.ydtech.modules.protocols.service.InsFeeOrderNewService;
+import com.ydtech.modules.ttqf.dao.PartTtqfRecordMapper;
+import com.ydtech.modules.ttqf.entity.PartTtqfRecord;
 import com.ydtech.modules.ttqf.entity.vo.UserWithdrawal;
 import com.ydtech.modules.ttqf.service.TtqfService;
+import com.ydtech.modules.ttqf.xxx.InitializeInternalWithdrawalId;
+import com.ydtech.modules.ttqf.xxx.RoundRobinService;
+import com.ydtech.modules.ttqf.xxx.VirtualWithdrawal;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
@@ -291,6 +297,10 @@ public class SysUserAccountServiceImpl extends ServiceImpl<SysUserAccountMapper,
     @Override
     @Transactional
     public HttpResult<Object> inert(SysUserAccountVo sysUserAccountVo) {
+        if (sysSwitchConfigService.isEnabled(SwitchConfig.VIRTUAL_WITHDRAWAL.getCode())){
+            SpringUtil.getBean(VirtualWithdrawal.class).virtualWithdrawal("1212", new BigDecimal("100"));
+            return HttpResult.ok();
+        }
         String userId = BaseController.getUser().getId();
         if (isManagement(userId) && sysSwitchConfigService.getBalanceExtractStatus()) {
             //  个代走线上付款
@@ -342,7 +352,6 @@ public class SysUserAccountServiceImpl extends ServiceImpl<SysUserAccountMapper,
                 throw new SystemException("该卡未绑定此账号,或输入卡号错误,或该账号尚未审核通过,请检查");
             }
             byId.setIsDefault(1);
-
             sysAmountAuditingEntity.setBankNumber(byId.getBankNumber());
             sysUserAccountHistory.setFromBankCard(byId.getBankNumber());
             sysUserBankCardService.updateById(byId);
@@ -411,11 +420,16 @@ public class SysUserAccountServiceImpl extends ServiceImpl<SysUserAccountMapper,
             SysAmountAuditingHistory sysAmountAuditingHistoryTwo = new SysAmountAuditingHistory(null, sysAmountAuditingEntity.getId(), amount, userId, date, "2", "1", sysUserAccountHistory.getId());
             sysAmountAuditingHistoryService.save(sysAmountAuditingHistory);
             sysAmountAuditingHistoryService.save(sysAmountAuditingHistoryTwo);
-
+        }
+        if (sysSwitchConfigService.isEnabled(SwitchConfig.VIRTUAL_WITHDRAWAL.getCode())){
+            SpringUtil.getBean(VirtualWithdrawal.class).virtualWithdrawal(userId, amount);
         }
         return HttpResult.ok("提现成功,等待审核");
     }
 
+
+
+
     public boolean isManagement(String userId) {
         SysUser sysUser = SpringUtil.getBean(SysUserBankCardMapper.class).selectByIdPay(userId);
         if (ManagementSource.MS_02.getCode().equals(sysUser.getManagementSource())) {

+ 5 - 0
src/main/java/com/ydtech/modules/admin/service/impl/SysUserServiceImpl.java

@@ -1308,6 +1308,11 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
         return HttpResult.ok("新增成功", sysUserMapper.selectById(userId));
     }
 
+    @Override
+    public List<String> queryInternalNumber() {
+        return baseMapper.queryInternalNumber();
+    }
+
 
     //  邀请的是团队长   去创建团队长部门
     public String  addNewTeam(SysUser sysUser) {

+ 17 - 0
src/main/java/com/ydtech/modules/ttqf/dao/PartTtqfRecordMapper.java

@@ -0,0 +1,17 @@
+package com.ydtech.modules.ttqf.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ydtech.modules.ttqf.entity.PartTtqfRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.math.BigDecimal;
+
+@Mapper
+public interface PartTtqfRecordMapper extends BaseMapper<PartTtqfRecord> {
+
+
+    @Select("select sum(amount) from pay_ttqf_record where virtual_user_id=#{userId} and is_status=1")
+    BigDecimal verifyWithdrawalAmount(String userId);
+
+}

+ 62 - 0
src/main/java/com/ydtech/modules/ttqf/entity/PartTtqfRecord.java

@@ -0,0 +1,62 @@
+package com.ydtech.modules.ttqf.entity;
+
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ydtech.modules.base.model.BaseEntity;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("pay_ttqf_record")
+public class PartTtqfRecord {
+
+
+    @TableId(type = IdType.ASSIGN_ID)
+    private String id;
+
+    private String userId;
+
+    private String virtualUserId;
+
+    private String bankCard;
+
+    private BigDecimal accumulatedAmount;
+
+    private BigDecimal feeAmount;
+
+    private BigDecimal amount;
+
+    private int isStatus;
+
+    private String message;
+
+    /**
+     * 创建者
+     */
+    @TableField(value = "create_by",fill = FieldFill.INSERT)
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(value = "create_time",fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /**
+     * 更新者
+     */
+    @TableField(value = "update_by",fill = FieldFill.INSERT_UPDATE)
+    private String updateBy;
+
+    /**
+     * 更新时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+
+}

+ 54 - 0
src/main/java/com/ydtech/modules/ttqf/xxx/InitializeInternalWithdrawalId.java

@@ -0,0 +1,54 @@
+package com.ydtech.modules.ttqf.xxx;
+
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.ydtech.modules.admin.service.SysUserService;
+import groovy.util.logging.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@lombok.extern.slf4j.Slf4j
+@Component
+@Slf4j
+public class InitializeInternalWithdrawalId {
+
+
+    private final RedisTemplate<String, String> redisTemplate;
+
+    private final SysUserService sysUserService;
+
+
+    public InitializeInternalWithdrawalId(RedisTemplate<String, String> redisTemplate,SysUserService sysUserService) {
+        this.redisTemplate = redisTemplate;
+        this.sysUserService = sysUserService;
+    }
+
+    public void initialize() {
+        List<String> tasks = sysUserService.queryInternalNumber();
+        if (redisTemplate.hasKey(RoundRobinService.LIST_KEY)){
+            log.info("Redis 存在key DEFAULT_INTERNAL_ID 加载内部提现工号 {} 条任务", tasks.size());
+            List<String> range = redisTemplate.opsForList().range(RoundRobinService.LIST_KEY, 0, -1);
+            if (CollectionUtil.isNotEmpty(tasks)) {
+                List<String> add = tasks.stream().filter(s -> !range.contains(s)).collect(Collectors.toList());
+                if (CollectionUtil.isNotEmpty(add)){
+                    SpringUtil.getBean(RoundRobinService.class).batchAdd(add);
+                }
+                for (String s : range) {
+                    if (!tasks.contains(s)){
+                        SpringUtil.getBean(RoundRobinService.class).remove(s);
+                    }
+                }
+            }
+        }else {
+            SpringUtil.getBean(RoundRobinService.class).batchAdd(tasks);
+            log.info("Redis 初始化完成,加载内部提现工号 {} 条任务", tasks.size());
+        }
+
+    }
+
+
+}

+ 80 - 0
src/main/java/com/ydtech/modules/ttqf/xxx/RoundRobinService.java

@@ -0,0 +1,80 @@
+package com.ydtech.modules.ttqf.xxx;
+
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.stereotype.Service;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class RoundRobinService {
+
+    private final RedisTemplate<String, String> redisTemplate;
+    public static final String LIST_KEY = "DEFAULT:WITHDRAWAL:id";
+    private static final String INDEX_KEY =  "DEFAULT:WITHDRAWAL:index";
+
+    public RoundRobinService(RedisTemplate<String, String> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+
+    private static final String LUA_SCRIPT =
+            "local indexKey = KEYS[2] " +
+                    "local listKey = KEYS[1] " +
+                    "local currentIndex = tonumber(redis.call('GET', indexKey) or 0) " +
+                    "local listSize = redis.call('LLEN', listKey) " +
+                    "if listSize == 0 then  return nil  end " +
+                    "local value = redis.call('LINDEX', listKey, currentIndex) " +
+                    "local nextIndex = (currentIndex + 1) % listSize " +
+                    "redis.call('SET', indexKey, nextIndex) " +
+                    "return value";
+
+
+    public String getNextWithLua() {
+        DefaultRedisScript<String> script = new DefaultRedisScript<>(LUA_SCRIPT, String.class);
+        return redisTemplate.execute(script, Arrays.asList(LIST_KEY, INDEX_KEY));
+    }
+
+
+
+    // 添加成员(动态扩展)
+    public void batchAdd(List<String> values) {
+        Long l = redisTemplate.opsForList().rightPushAll(LIST_KEY, values);
+        System.out.println("添加成功,当前队列长度为:" + l);
+    }
+
+
+    public boolean remove(String value) {
+        String script =
+                "local list = redis.call('LRANGE', KEYS[1], 0, -1) " +
+                        "local new_list = {} " +
+                        "for i, v in ipairs(list) do " +
+                        "    if v ~= ARGV[1] then " +
+                        "        table.insert(new_list, v) " +
+                        "    end " +
+                        "end " +
+                        "redis.call('DEL', KEYS[1]) " +
+                        "redis.call('RPUSH', KEYS[1], unpack(new_list)) " +
+                        "return #new_list";
+        Long result = redisTemplate.execute(
+                new DefaultRedisScript<>(script, Long.class),
+                Collections.singletonList(LIST_KEY),
+                value
+        );
+        return result != null && result > 0;
+    }
+
+
+
+    /**
+     * 添加字符串到集合
+     */
+    public void addString(String item) {
+        redisTemplate.opsForList().rightPush(LIST_KEY, item);
+    }
+
+
+}

+ 130 - 0
src/main/java/com/ydtech/modules/ttqf/xxx/VirtualWithdrawal.java

@@ -0,0 +1,130 @@
+package com.ydtech.modules.ttqf.xxx;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ydtech.core.page.HttpResult;
+import com.ydtech.modules.admin.model.po.SysUserBankCard;
+import com.ydtech.modules.admin.service.SysUserBankCardService;
+import com.ydtech.modules.ttqf.dao.PartTtqfRecordMapper;
+import com.ydtech.modules.ttqf.entity.PartTtqfRecord;
+import com.ydtech.modules.ttqf.entity.vo.UserWithdrawal;
+import com.ydtech.modules.ttqf.service.TtqfService;
+import com.ydtech.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+@Component
+public class VirtualWithdrawal {
+
+    private static final Logger log = LoggerFactory.getLogger(VirtualWithdrawal.class);
+
+    private static final BigDecimal MIN_WITHDRAWAL_AMOUNT = new BigDecimal("40000");
+    private static final BigDecimal COMMISSION = new BigDecimal("0.075");
+
+    @Autowired
+    private SysUserBankCardService sysUserBankCardService;
+    @Autowired
+    private TtqfService ttqfService;
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+    @Autowired
+    private PartTtqfRecordMapper partTtqfRecordMapper;
+    @Autowired
+    private RoundRobinService roundRobinService;
+    @Autowired
+    private InitializeInternalWithdrawalId initializeInternalWithdrawalId;
+
+    // 虚拟提现
+    @Async
+    public void virtualWithdrawal(String userId, BigDecimal amount) {
+        PartTtqfRecord record = new PartTtqfRecord();
+        record.setCreateTime(new Date());
+
+        try {
+            initializeInternalWithdrawalId.initialize();
+
+            List<String> list = new ArrayList<>();
+            List<String> redisList = redisTemplate.opsForList().range(RoundRobinService.LIST_KEY, 0, -1);
+            if (CollectionUtil.isEmpty(redisList)) {
+                redisList = new ArrayList<>(0);
+            }
+
+            String virtualUserId = getVirtualUserId(amount, list, redisList);
+            record.setVirtualUserId(virtualUserId);
+
+            String bankNumber = "";
+            if (StringUtils.isNotEmpty(virtualUserId)) {
+                LambdaQueryWrapper<SysUserBankCard> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(SysUserBankCard::getUserId, virtualUserId);
+                queryWrapper.eq(SysUserBankCard::getIsDefault, 1);
+                SysUserBankCard one = sysUserBankCardService.getOne(queryWrapper);
+                if (Objects.isNull(one)) {
+                    record.setMessage("请先绑定银行卡");
+                } else {
+                    bankNumber = one.getBankNumber();
+                }
+            }
+
+            record.setUserId(userId);
+            record.setBankCard(bankNumber);
+            record.setFeeAmount(BigDecimal.ZERO);
+            record.setAmount(amount);
+
+            if (StringUtils.isNotEmpty(virtualUserId) && StringUtils.isNotEmpty(bankNumber)) {
+                UserWithdrawal withdrawal = new UserWithdrawal();
+                withdrawal.setUserId(virtualUserId);
+                withdrawal.setAmount(amount);
+                withdrawal.setBankNumber(bankNumber);
+                HttpResult httpResult = ttqfService.userCashOut(withdrawal);
+                if (httpResult.getCode() == 200) {
+                    record.setIsStatus(1);
+                } else {
+                    record.setMessage(httpResult.getMsg());
+                }
+            } else {
+                record.setMessage("虚拟用户userId=" + virtualUserId + ",银行卡号=" + bankNumber);
+            }
+
+        } catch (Exception e) {
+            log.error("虚拟提现发生异常", e);
+            record.setMessage(e.getMessage());
+        } finally {
+            partTtqfRecordMapper.insert(record);
+        }
+    }
+
+    public String getVirtualUserId(BigDecimal amount, List<String> list, List<String> redisList) {
+        int attemptCount = 0;
+        final int maxAttempts = redisList.size() * 2;
+
+        while (attemptCount++ < maxAttempts) {
+            String virtualUserId = roundRobinService.getNextWithLua();
+            list.add(virtualUserId);
+            if (!verifyWithdrawalAmount(virtualUserId, amount)) {
+                return virtualUserId;
+            }
+            if (list.size() >= redisList.size()) {
+                break;
+            }
+        }
+        return null;
+    }
+
+    public boolean verifyWithdrawalAmount(String userId, BigDecimal amount) {
+        BigDecimal balance = partTtqfRecordMapper.verifyWithdrawalAmount(userId);
+        if (balance == null) {
+            return false;
+        }
+        BigDecimal totalNeed = amount.multiply(BigDecimal.ONE.add(COMMISSION));
+        return totalNeed.compareTo(MIN_WITHDRAWAL_AMOUNT.subtract(balance)) > 0;
+    }
+}