
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