All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.neko233.skilltree.redemption_code.RedemptionCodeGenerator Maven / Gradle / Ivy

package com.neko233.skilltree.redemption_code;

import com.neko233.skilltree.commons.sql.SqlTemplate233;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 兑换码生成器
 */

@Slf4j
public class RedemptionCodeGenerator {

    private final DataSource dataSource;
    private final int codeLength;
    private final int maxRetryCount;

    private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static final SecureRandom RANDOM = new SecureRandom();

    public RedemptionCodeGenerator(DataSource dataSource,
                                   Integer codeLength,
                                   Integer maxRetryCount) {
        this.dataSource = dataSource;
        this.codeLength = Optional.ofNullable(codeLength).orElse(10);
        this.maxRetryCount = Optional.ofNullable(maxRetryCount).orElse(10);

        createTableIfNotExists();
    }


    public void createTableIfNotExists() {
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(
                     "CREATE TABLE IF NOT EXISTS redemption_codes (" +
                             "id INT AUTO_INCREMENT PRIMARY KEY," +
                             "businessType VARCHAR(255) NOT NULL," +
                             "code VARCHAR(255) NOT NULL," +
                             "maxUseCount INT NOT NULL comment '最大使用次数'," +
                             "haveUseCount INT default 0 comment '已用次数'," +
                             " unique key (businessType, code)" +
                             ") engine = Innodb, charset = utf8mb4")) {
            stmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static RedemptionCodeGenerator create(DataSource dataSource) {
        return new RedemptionCodeGenerator(dataSource, null, null);
    }

    public List generateRedemptionCodes(String businessType,
                                                int generateCount,
                                                int maxUseCount) {
        List codes = new ArrayList<>();

        for (int i = 0; i < generateCount; i++) {
            String code = generateUniqueCode(businessType, codes, maxUseCount);
            codes.add(code);

        }
        return codes;
    }

    private String generateUniqueCode(String businessType,
                                      List existingCodes,
                                      int maxUseCount) {
        for (int i = 0; i < maxRetryCount; i++) {
            String code = generateRandomCode();
            boolean isNotExistInThisGenerator = !existingCodes.contains(code);
            boolean isInsertDbSuccess = saveCodeToDatabase(businessType, code, maxUseCount);
            if (isNotExistInThisGenerator && isInsertDbSuccess) {
                return code;
            }
        }
        throw new RuntimeException("Unable to generate unique redemption code.");
    }

    private String generateRandomCode() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < codeLength; i++) {
            int index = RANDOM.nextInt(ALPHABET.length());
            char randomChar = ALPHABET.charAt(index);
            sb.append(randomChar);
        }
        return sb.toString();
    }

    private boolean saveCodeToDatabase(String businessType,
                                       String code,
                                       int maxUseCount) {
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(
                     "INSERT INTO redemption_codes (businessType, code, maxUseCount) VALUES (?, ?, ?)")) {
            stmt.setString(1, businessType);
            stmt.setString(2, code);
            stmt.setInt(3, maxUseCount);
            int i = stmt.executeUpdate();
            if (i > 0) {
                return true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    public long selectCountByBusinessType(String businessType) {

        final String SELECT_COUNT_BY_BIZ = "SELECT COUNT(*) FROM redemption_codes WHERE businessType = ? ";

        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(
                     SELECT_COUNT_BY_BIZ)) {
            stmt.setString(1, businessType);
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    return rs.getLong(1);
                }
            }
        } catch (SQLException e) {
            log.error("select count for 兑换码错误, sql = {}", SELECT_COUNT_BY_BIZ, e);
        }
        return 0;
    }

    /**
     * 消耗兑换码
     *
     * @param businessType 业务名
     * @param code         兑换码
     * @return 是否成功
     */
    public boolean consumeCode(String businessType,
                               String code) {
        String sql = "UPDATE redemption_codes SET haveUseCount = haveUseCount + 1 WHERE businessType = ${businessType} AND code = ${code}" +
                " and haveUseCount + 1 <= maxUseCount ";
        String updateSql = SqlTemplate233.builder(sql)
                .put("businessType", businessType)
                .put("code", code)
                .build();

        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(updateSql)) {
            int i = stmt.executeUpdate();
            if (i > 0) {
                return true;
            }
        } catch (SQLException e) {
            log.error("兑换码 update 错误, sql = {}", updateSql, e);
        }
        return false;
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy