一.小组成员及分工
成员名 | 完成部分 | 备注 |
刘亚楠 | 图形化界面,统计并显示获奖情况,测试代码,git上传,团队博客撰写用户注册登录,用户名、密码余额等信息管理 | 组长 |
刘启彤 | 管理员登录及管理员抽奖功能,彩票信息记录,用户抽奖功能 | 组员 |
二.项目简介
功能要求:模拟福利彩票36选7,实现彩票抽奖与中奖通知功能,需完成如下功能:
1.允许注册用户
用户信息包括用户名,密码,账户金额,电话号码等信息
2.允许注册用户购买彩票
用户能进行手动选号、随机选号,并允许设置投掷数
3.抽奖功能
(1)屏幕上显示抽奖号码的滚动过程,当按”停止“时产生抽奖结果
(2)特等奖:7个号码全中
一等奖:中6个号码
(3)抽出号码后,显示用户名和中奖信息
4.彩票通知功能
抽出号码后用户登录系统,系统显示是否中奖
5.测试功能
(1)设计一自动注册用户(10w个),能够自动买彩票
(2)模拟抽奖,测试抽奖程序的正确性
三.功能架构
四.项目需求分析
1.注册用户
编写对应的Gui,输入用户信息,并将用户信息按一定的格式写入文件,成功注册跳出成功注册的消息提示
2.登录
输入id和密码后进入用户彩票购买页面,密码进行了加密,一定程度上提高了系统的安全性
3.购买彩票
用户可以选择手动选号或者随机选号,随机选号采用生成随机数功能
4.中奖信息
用户可以在这里查看是否中奖
5.管理员开奖
采用滚动生成随机数的方式进行彩票号码随机数的生成,按停止按钮停止
6.自动生成
自动注册用户、自动购买彩票以及模拟抽奖
五.项目采用技术,主要功能
(一)代码展示
1.Admin包
该包用于管理员储存信息的管理,及管理员的抽号功能
1)Admin类
储存管理员名及密码,用于管理员登录
package Admin;
public class Admin {
private String adminName;
private String adminPassword;
public Admin(String adminName, String adminPassword) {
this.adminName = adminName;
this.adminPassword = adminPassword;
}
public String getAdminName() {
return adminName;
}
public void setAdminName(String adminName) {
this.adminName = adminName;
}
public String getAdminPassword() {
return adminPassword;
}
public void setAdminPassword(String adminPassword) {
this.adminPassword = adminPassword;
}
// 验证管理员登录
public static boolean validateAdmin(String adminName, String adminPassword) {
return adminName.equals("admin") && adminPassword.equals("123456");
}
}
2)Rolling类
a. Rolling 模拟滚动并显示滚动号码
b. displayResult 屏幕上显示中奖结果
c. displayRollingNumber 自动生成中奖号码
d. setWinningNumber 判断抽中的号码是否合法
e. createJframe 图形化界面设置
package Admin;
import java.util.Random;
import javax.swing.*;
import lotterySystem.Show;
import lotterySystem.Winning;
import java.awt.BorderLayout;
import java.util.List;
//负责实现彩票抽奖的逻辑,包括生成中奖号码、比对用户选择的号码等
public class Rolling {
//自动生成中奖号码
public interface RollingCallback {
void onRollingFinished(String winningNumber);
}
private static Thread rollingThread;
private static JLabel numberLabel;
private static String winningNumber;
private static volatile boolean rolling = true; // 用于控制是否滚动
public static String rolling(RollingCallback callback) {
JFrame frame = new JFrame("抽奖游戏");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("开始");
JButton stopButton = new JButton("停止");
numberLabel = new JLabel("", SwingConstants.CENTER);
// 开始按钮的点击事件监听器
startButton.addActionListener(e -> {
rolling = true;//开始滚动
// 创建一个新线程来模拟滚动过程
Thread rollingThread = new Thread(() -> {
while (rolling) {
String number = generateWinningNumber();
displayRollingNumber(number); // 在屏幕上显示滚动中的号码
try {
Thread.sleep(100); // 模拟滚动速度
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
// 启动滚动线程
rollingThread.start();
});
// 停止按钮的点击事件监听器
stopButton.addActionListener(e -> {
rolling = false; // 停止滚动
winningNumber = generateWinningNumber(); // 生成中奖号码
displayResult(winningNumber);
// 如果提供了回调函数,则调用它并改变窗口的关闭操作
if (callback != null) {
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
callback.onRollingFinished(winningNumber); // 调用回调函数通知抽奖结束
}
});
if (rollingThread != null && rollingThread.isAlive()) {
try {
rollingThread.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
numberLabel.setText(""); // 清空号码显示
}
// 创建一个JPanel并将开始和停止按钮添加到其中
JPanel panel=new JPanel();
panel.add(startButton);
panel.add(stopButton);
// 将panel添加到frame的南部(底部)
frame.add(panel, BorderLayout.SOUTH);
// 将numberLabel添加到frame的中心位置
frame.add(numberLabel, BorderLayout.CENTER);
// 设置窗口的大小并使其可见
frame.setSize(300, 200);
frame.setVisible(true);
return winningNumber;
}
public static void displayResult(String winningNumber) {
// 使用 SwingUtilities.invokeLater 来确保在事件调度线程(EDT)上更新 GUI 组件
SwingUtilities.invokeLater(() ->
// 使用 lambda 表达式来更新 numberLabel 的文本
numberLabel.setText("\n中奖号码是:【" +
// 使用正则表达式替换每两个数字为一个数字后跟一个逗号
winningNumber.replaceAll("(\\d{2})", "$1,") + "】"));
}// 在屏幕上展示中奖结果
public static void displayRollingNumber(String number) {
// 同样使用 SwingUtilities.invokeLater 来确保在 EDT 上更新 GUI 组件
SwingUtilities.invokeLater(() ->
// 使用 lambda 表达式来更新 numberLabel 的文本
numberLabel.setText("抽奖中:【" +
// 使用正则表达式替换每两个数字为一个数字后跟一个逗号
number.replaceAll("(\\d{2})", "$1,")+ "】"));
}
// 自动生成中奖号码
public static String generateWinningNumber() {
Random random = new Random();
// 创建一个 StringBuilder 对象来构建中奖号码字符串
StringBuilder sb = new StringBuilder();
// 循环 7 次以生成 7 个数字
for (int i = 0; i < 7; i++) {
// 生成一个 1 到 36 之间的随机整数
int number = random.nextInt(36) + 1;
// 使用 String.format 将数字格式化为两位宽度,并在前面补 0
sb.append(String.format("%02d", number));
}
return sb.toString();// 返回构建好的中奖号码字符串
}
//判断中将号码是否合法
public static boolean setWinningNumber(String number) {
// 使用正则表达式检查输入的号码是否由14个数字组成
if(!number.matches("\\d{14}")) {
// 如果不匹配,则弹出对话框提示用户号码不合法
JOptionPane.showMessageDialog(null, "号码不合法,请重新选择");
// 返回false表示设置失败
return false;}
// 遍历输入的号码字符串,每次步进2个字符
for (int i = 0; i < number.length(); i += 2) {
// 截取当前位置开始的两个字符作为子字符串
String subNum = number.substring(i, i + 2);
// 将子字符串转换为整数
int numValue = Integer.parseInt(subNum);
// 检查这个整数是否在1到36的范围内
if (numValue < 1 || numValue > 36) {
// 如果不在范围内,则弹出对话框提示用户号码不合法
JOptionPane.showMessageDialog(null, "号码不合法,请重新选择");
// 返回false表示设置失败
return false;
}
}
winningNumber = number;
JOptionPane.showMessageDialog(null, "设置成功,中将号码为:【"+winningNumber.replaceAll("(\\d{2})", "$1,")+ "】");
return true;
}
public static void createJframe(List<String> userRecords){
// 创建一个新的JFrame窗口,标题为“抽奖游戏”
JFrame frame = new JFrame("抽奖游戏");
// 设置窗口关闭时的操作为退出程序
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 创建开始和停止按钮
JButton startButton = new JButton("开始");
JButton stopButton = new JButton("停止");
numberLabel = new JLabel("", SwingConstants.CENTER);
// 为开始按钮添加事件监听器
startButton.addActionListener(e -> {
// 设置rolling为true,启动滚动
rolling = true;
// 创建一个新线程来模拟号码滚动
Thread rollingThread = new Thread(() -> {
while (rolling) {
// 生成一个滚动中的号码
String number = generateWinningNumber();
// 在屏幕上显示滚动中的号码
displayRollingNumber(number);
try {
Thread.sleep(100); // 模拟滚动速度
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
// 启动滚动线程
rollingThread.start();
});
// 为停止按钮添加事件监听器
stopButton.addActionListener(e -> {
rolling = false; // 停止滚动
winningNumber = generateWinningNumber(); // 生成中奖号码
displayResult(winningNumber);
// if (callback != null) {
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JOptionPane.showMessageDialog(frame, "抽奖结束,中奖号码:"+ winningNumber);
Show.displayWinnerInfos(Winning.checkWinning(userRecords, winningNumber),winningNumber);
// 继续执行其他逻辑...
// callback.onRollingFinished(winningNumber); // 调用回调函数通知抽奖结束
// }
if (rollingThread != null && rollingThread.isAlive()) {
try {
rollingThread.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
numberLabel.setText(""); // 清空号码显示
}
});
// 创建一个面板并将按钮添加到面板中
JPanel panel=new JPanel();
panel.add(startButton);
panel.add(stopButton);
// 将面板添加到frame的底部
frame.add(panel, BorderLayout.SOUTH);
frame.add(numberLabel, BorderLayout.CENTER);
// 设置frame的大小并使其可见
frame.setSize(300, 200);
frame.setVisible(true);
return;
}
}
2.lotterySystem包
该包用于显示用户的获奖信息,包括当前用户和所有用户,并对比用户号码是否中奖及计算奖项
1)Show类
a. displayNumbers 显示该用户获奖信息
b.displayWinnerInfos 显示所有中奖者的用户信息
package lotterySystem;
import java.awt.BorderLayout;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import user.User;
public class Show {
//显示当前用户的彩票号数和获奖情况,
public static void displayNumbers(List<String> numbers, User user) {
// 创建一个新的JFrame窗口,标题为用户名加上'的彩票号数和获奖情况'
JFrame frame = new JFrame(user.getUsername() + "'s Numbers and Winning Status");
// 设置窗口关闭时的操作为销毁窗口而不是退出程序
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // 设置关闭操作为销毁窗口
// 创建一个新的JPanel面板,使用BorderLayout布局
JPanel panel = new JPanel(new BorderLayout());
// 定义表格的列名,分别为“彩票号码”和“获奖情况”
String[] columnNames = {"彩票号码", "获奖情况"};
// 初始化二维数组data来存储表格数据,大小为numbers列表的长度
Object[][] data = new Object[numbers.size()][2];
// 遍历numbers列表,将每个彩票号码和获奖情况填充到data数组中
for (int i=0; i<numbers.size();i++){
// 将当前彩票号码按逗号分割成数组
String[] numberInfo=numbers.get(i).split(",");
// 将彩票号码添加到表格的第一列,并添加括号装饰
data[i][0] ="【"+ numberInfo[0]+"】";
// 检查获奖情况是否为“未中奖”
if(numberInfo.length>1){
// 如果是“未中奖”,则添加到表格的第二列
if(numberInfo[1].equals("未中奖")){
// 如果是“未中奖”,则添加到表格的第二列
data[i][1]="未中奖";
}else{
// 如果不是“未中奖”,则直接添加其他获奖情况到表格的第二列
data[i][1]=numberInfo[1];
}
}else{
// 如果分割后的数组长度不大于1(即没有获奖情况),则默认添加“未开奖”
data[i][1]="未开奖";
}
}
// 创建一个新的JTable,使用data和columnNames初始化
JTable table=new JTable(data,columnNames);
// 创建一个JScrollPane,将JTable添加到JScrollPane中,以便可以滚动查看表格
JScrollPane scrollPane=new JScrollPane(table);
// 将JScrollPane添加到面板的中间区域
panel.add(scrollPane,BorderLayout.CENTER);
// 将面板添加到frame中
frame.add(panel);
// 设置frame的大小
frame.setSize(400, 300);
// 设置frame为可见
frame.setVisible(true);
}
//显示所有获奖者的获奖信息
public static void displayWinnerInfos(List<String> winnerInfos, String winningNumber) {
// 创建一个新的JFrame实例,作为中奖信息显示的对话框,并设置标题为"Lottery Winners"
JFrame dialog = new JFrame("Lottery Winners");
// 设置窗口的默认关闭操作为销毁窗口,即关闭窗口时释放相关资源
dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // 设置关闭操作为销毁窗口
// 创建一个新的JPanel面板,并使用BorderLayout布局管理器
JPanel panel = new JPanel(new BorderLayout());
// 创建一个JLabel,用于显示中奖号码,并将其添加到面板的北部区域
JLabel winningLabel = new JLabel("中奖号码:" + winningNumber);
panel.add(winningLabel, BorderLayout.NORTH);
// 定义表格的列名,分别为"用户名"、"彩票号码"和"奖项"
String[] columnNames={"用户名","彩票号码","奖项"};
// 初始化一个二维数组data,用于存储中奖者信息的表格数据
// 数组大小为winnerInfos列表的长度,每个元素包含三个字段(用户名、彩票号码、奖项)
Object[][] data=new Object[winnerInfos.size()][3];
// 遍历winnerInfos列表,解析每个中奖者信息,并填充到data数组中
for(int i=0;i<winnerInfos.size();i++) {
String[] minInfo=winnerInfos.get(i).split(",");
// 将用户名、彩票号码、奖项分别填充到data数组的对应位置
data[i][0] = minInfo[0]; // 用户名
data[i][1] = minInfo[1]; // 彩票号码
data[i][2] = minInfo[2]; // 奖项
}
// 创建一个新的JTable实例,并使用data和columnNames初始化
JTable table=new JTable(data,columnNames);
// 创建一个JScrollPane实例,将JTable添加到JScrollPane中,以便于滚动查看
JScrollPane scrollPane=new JScrollPane(table);
// 将JScrollPane添加到面板的中心区域
panel.add(scrollPane,BorderLayout.CENTER);
// 将面板添加到JFrame中
dialog.add(panel);
// 设置窗口的大小
dialog.setSize(400, 300);
// 设置窗口为可见状态
dialog.setVisible(true);
}
}
2)Winning类
a. checkWinning 对比用户选择的号码是否中奖
b. calculateResult 计算奖项
c. notifyFirstLogin 图形化界面并计算奖金
package lotterySystem;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import record.DataStorage;
import record.NumbersRecord;
import user.User;
public class Winning {
// 比对用户选择的号码是否中奖
public static List<String> checkWinning(List<String> userRecords, String winningNumber) {
// 创建一个新的ArrayList来存储中奖信息
List<String> winningInfos = new ArrayList<>();
// 遍历用户购买彩票的记录
for (String record : userRecords) {
// 使用逗号分割用户记录,得到用户名和彩票号码列表(这里假设record的格式是"用户名,彩票号码1,彩票号码2,...")
String[] userInfo = record.split(",");
String perName = userInfo[0];
// 从NumbersRecord类中获取该用户购买的所有彩票号码列表
List<String> numbersOfPerUser = NumbersRecord.fileToList(perName);
// 创建一个新的ArrayList来存储更新后的彩票号码及其对应的中奖结果
List<String> updateNumbers = new ArrayList<>();
// 遍历该用户购买的所有彩票号码
for (String perNumber : numbersOfPerUser) {
// 使用逗号分割彩票号码记录,得到彩票号码和可能的其他信息(假设perNumber的格式是"彩票号码,其他信息")
String[] numberInfo = perNumber.split(",");
// 创建一个StringBuilder对象来构建组合后的彩票号码字符串(这里假设彩票号码是由每两个字符组合而成的)
StringBuilder combinedNumbers = new StringBuilder();
// 遍历彩票号码的每一个字符,每两个字符组合成一个号码片段,并用空格分隔
for (int i = 0; i < numberInfo[0].length(); i += 2) {
combinedNumbers.append(numberInfo[0], i, i + 2).append(" ");
}
// 调用calculateResult方法计算该彩票号码是否中奖,并返回结果
String result = calculateResult(combinedNumbers.toString().trim(), winningNumber);
// 如果中奖结果不是"未获奖",则构建中奖信息字符串并添加到winningInfos列表中
if (!result.equals("未获奖")) {
String winningInfo = perName + "," + numberInfo[0] + "," + result;
winningInfos.add(winningInfo);
}
// 构建更新后的彩票号码信息(包含原始号码和中奖结果),并添加到updateNumbers列表中
updateNumbers.add(numberInfo[0] + "," + result);
}
// 调用NumbersRecord类的listToFile方法,将更新后的彩票号码信息写回到文件中
NumbersRecord.listToFile(updateNumbers, perName);
}
// 返回中奖用户的信息列表
return winningInfos;
}
//计算奖项
private static String calculateResult(String perNumber, String winningNumber) {
int matchedCount = 0;
for (int i = 0; i < perNumber.length(); i += 3) { // 每两位数字之间有一个空格,所以步长为3
if (winningNumber.contains(perNumber.substring(i, i + 2))) {
matchedCount++;
}
}
if (matchedCount == 7) {
return "特等奖";
} else if (matchedCount == 6) {
return "一等奖";
} else if (matchedCount == 5){
return "二等奖";
} else if(matchedCount ==4){
return "三等奖";
} else{
return "未中奖";
}
}
public static void notifyFirstLogin(List<String> numbers, User currentUser,List<String> userRecords,JLabel balanceLabel) {
if(numbers.size()==0){
JOptionPane.showMessageDialog(null, "欢迎来到大乐透抽奖平台,在这里一夜暴富不是梦!!!\n 快去购买你的第一张彩票吧");
return;
}
int totalPrize = 0;
StringBuilder message = new StringBuilder();
boolean flag=true;
for (String number : numbers) {
int price=0;
String[] numberInfo = number.split(",");
if(!numberInfo[1].equals("未中奖")){
message.append(numberInfo[0]).append("\n恭喜用户:").append(currentUser.getUsername()).append(":\n");
message.append("中将号码 ").append(numberInfo[0]).append(" 获得 ").append(numberInfo[1]);
flag=false;
}
if(numberInfo[1].equals("特等奖")){
price=1000;
}else if (numberInfo[1].equals("一等奖")) {
price=500;
} else if (numberInfo[1].equals("二等奖")) {
price=250;
} else if (numberInfo[1].equals("三等奖")){
price=100;
}
// 计算总奖金
totalPrize += price;
}
if(flag){
message.append("很遗憾,您购买的彩票均未获奖!");
}
// 弹出消息框
int option = JOptionPane.showOptionDialog(null, message.toString(), "中将提醒", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
null, new Object[]{"领取奖金"}, "领取");
if (option == 0) { // 如果点击了“领取”按钮
JOptionPane.showMessageDialog(null, "已成功领取总共 " + totalPrize + " 元", "领取成功", JOptionPane.INFORMATION_MESSAGE);
double amount=Double.parseDouble(Integer.toString(totalPrize) );
currentUser.recharge(currentUser, amount,userRecords);
DataStorage.updateBalance(currentUser.getUsername(),currentUser.getBalance(),userRecords);
balanceLabel.setText("余额:" + currentUser.getBalance());// 删除文件里对应号码
File file = new File(currentUser.getUsername()+"_number_records.txt"); // 替换为实际文件路径
try {
List<String> lines = Files.readAllLines(file.toPath());
lines.removeAll(numbers);
Files.write(file.toPath(), lines);
numbers.clear(); // 清空列表里对应号码
} catch (IOException e) {
e.printStackTrace();
}
}
}
// private static boolean isFirstTimeLogin(String username) {
// return true;
// }
}
3.record包
该包将注册的用户名及用户信息放入文件中,管理并读取用户的信息。同时通过文件记录彩票并管理彩票信息
1)DataStorage类
a. checkCredentials 判断输入的用户名是否符合要求
b. register 进行注册用户
c. saveUser 将用户信息保存到文件
d. loginUser 登录
e. updateBalance 更新用户余额
f. readUserRecrods 读取全部用户信息
g. formatRecord 生成用户信息的字符串
package record;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import user.User;
public class DataStorage {
//定义一个私有静态常量,表示用户数据文件的名称
private static final String USERS_FILE = "users.txt";
// 定义一个静态方法,用于检查给定的用户名是否存在于用户记录列表中
public static boolean checkCredentials(String username,List<String> userRecords) {
// 遍历用户记录列表
for (String record : userRecords) {
// 使用逗号作为分隔符,将记录分割成一个字符串数组
// 假设每条记录都是"username,password"的格式
String[] userInfo = record.split(",");
// 提取用户名(即数组的第一个元素)
// 并检查它是否与给定的用户名匹配
if (userInfo[0].equals(username)) {
// 如果找到匹配的用户名,则返回true
return true;
}
}
// 如果遍历完整个列表都没有找到匹配的用户名,则返回false
return false;
}
//注册
public static int register(String username, String password,String telnumber,List<String> userRecords) {
// 检查用户名是否已存在
if (checkCredentials(username,userRecords)) {
System.out.println("该用户名已被注册,请选择其他用户名。");
return 0;
}
// 检查密码长度是否为6位
if (password.length() != 6) {
System.out.println("密码必须为6位,请重新输入。");
return -1;
}
// 创建一个新用户对象
User newUser = new User(username, password, telnumber);
// 将新用户的信息格式化为字符串并添加到用户记录列表中
userRecords.add(formatRecord(newUser));
// 保存新用户信息(这里假设saveUser是另一个方法,用于持久化用户信息)
saveUser(newUser);
// 创建存储用户号码记录的文件路径(这里假设是用户名_number_records.txt)
String path = username+"_number_records.txt";
// 根据路径创建文件对象
File file = new File(path);
// 检查文件是否存在
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
System.out.println("系统错误");
} // 如果文件不存在,则创建新文件
System.out.println("File created successfully.");
}
// 注册成功,返回1
return 1;
}
// 将新用户信息保存到文件中
public static void saveUser(User user) {
try {
// 创建一个BufferedWriter对象,用于写入文件
// 首先,通过OutputStreamWriter将字节流转换为字符流,并指定字符编码为UTF-8
// 然后,通过FileOutputStream指定要写入的文件为USERS_FILE(该常量在DataStorage类中定义)
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(USERS_FILE),"UTF-8"));
// 将用户信息(用户名、密码、余额和电话号码)拼接成一个字符串,并使用逗号分隔
// 调用write方法将该字符串写入到文件中
writer.write(user.getUsername() + "," + user.getPassword() + "," + user.getBalance()+"," + user.getTelnumber());
// 写入一个新行符,以确保每条用户记录占一行
writer.newLine();
// 关闭BufferedWriter对象,释放相关资源
writer.close();
// 如果在文件写入过程中发生IOException异常,则捕获该异常并打印堆栈跟踪
} catch (IOException e) {
e.printStackTrace();
}
}
//登录
public static User loginUser(String username,String password,List<String> userRecords){
// 遍历用户记录列表
for(String record:userRecords){
// 使用逗号作为分隔符,将记录拆分为字符串数组
// 假设每个记录都包含用户名、密码、余额和电话号码,用逗号分隔
String[] userInfo=record.split(",");
// 检查当前记录的用户名和密码是否与登录的用户名和密码匹配
if(userInfo[0].equals(username)&&userInfo[1].equals(password)){
return new User(userInfo[0], userInfo[1], Double.parseDouble(userInfo[2]),userInfo[3]);
}
}
// 如果遍历完所有记录都没有找到匹配的用户名和密码,则返回null
return null;
}
//更新余额
public static void updateBalance(String username, double newBalance,List<String> userRecords) {
// 创建一个新的列表来存储更新后的用户记录
List<String> updatedRecords = new ArrayList<>();
for (String record : userRecords) {
String[] userInfo = record.split(",");
// 检查当前记录的用户名是否与要更新的用户名匹配
if (userInfo[0].equals(username)) {
// 更新特定用户的余额
// 如果匹配,则调用formatRecord方法(该方法未在提供的代码段中定义)
// 来格式化新的记录,其中包括更新后的余额
// formatRecord方法预计接受用户名、密码、新余额和电话号码作为参数
// 注意:这里假设密码字段userInfo[1]在更新余额时不需要改变
String updatedRecord = formatRecord(username, userInfo[1], newBalance,userInfo[3]);
updatedRecords.add(updatedRecord);
} else {
// 则将原始记录添加到updatedRecords列表中,不做任何修改
updatedRecords.add(record);
}
}
userRecords=updatedRecords;
try (BufferedWriter writer = new BufferedWriter(new FileWriter(USERS_FILE))) {
for (String userRecord : userRecords) {
// 将每个用户记录写入文件
writer.write(userRecord);
// 在每个用户记录后添加一个新行,确保每条记录都单独占一行
writer.newLine();
}
} catch (IOException e) {
// 如果在写入文件时发生IOException异常,则捕获该异常并打印堆栈跟踪
e.printStackTrace();
}
}
//读取全部用户信息
public static List<String> readUserRecrods(){
List<String> records=new ArrayList<>();
try{
BufferedReader reader=new BufferedReader(
new InputStreamReader(
new FileInputStream(USERS_FILE),"UTF-8"));
String line;
// 使用while循环逐行读取文件,直到读到文件末尾(返回null)
while((line=reader.readLine())!=null){
// 将读取到的每一行添加到records列表中
records.add(line);
}
// 显式关闭reader(但注意,如果使用try-with-resources则不需要手动关闭)
reader.close();
}catch(IOException e){
// 如果在读取文件过程中发生IO异常,则捕获该异常并打印堆栈跟踪
e.printStackTrace();
}
// 返回包含所有用户记录的列表
return records;
}
//生成某用户信息字符串
private static String formatRecord(String username,String password,double balance,String telnumber){
return username+","+password+","+balance+","+telnumber;
}
private static String formatRecord(User user) {
return user.getUsername() + "," + user.getPassword() + "," + user.getBalance() + "," + user.getTelnumber();
}}
2)NumbersRecord类
a. addNumberstoList 将新彩票加入number的List容器中
b. saveNumbersToFile 将用户选择的彩票号保存到对应用户的彩票文件中
c. fileToList 将彩票信息从文件中读到List中
d.listToFile 将彩票信息从List中读到文件中,并覆盖原有内容
package record;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
//彩票记录类
//保存彩票号码和中将信息
public class NumbersRecord {
private static final String NUMBER_RECORDS_FILE = "_number_records.txt";
// private static final String WINNING_RECORDS_FILE = "winning_records.txt";
// 将新彩票加入List<String> numbers
public static void addNumberstoList(List<String> numbers, List<String> newNumbers) {
for (String newNumber : newNumbers) {
numbers.add(newNumber + "," + "未获奖");
}
}
// 将新彩票保存到对应用户的彩票文件中
public static void saveNumbersToFile(String username, List<String> newNumbers) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(username + NUMBER_RECORDS_FILE, true))) {
for (String newNumber : newNumbers) {
writer.write(newNumber + "," + "未获奖");
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 将彩票信息从文件中读到List
public static List<String> fileToList(String username){
List<String> numbers = new ArrayList<>();
try{
String path =username+NUMBER_RECORDS_FILE;
BufferedReader reader=new BufferedReader(
new InputStreamReader(
new FileInputStream(path),"UTF-8"));
String line;
while((line=reader.readLine())!=null){
System.out.println(line);
numbers.add(line);
}
reader.close();
}catch(IOException e){
e.printStackTrace();
}
return numbers;
}
// 将彩票信息从List中读到文件,并覆盖原有内容
public static void listToFile(List<String> numbers, String username) {
try {
BufferedWriter writer=new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(username+NUMBER_RECORDS_FILE),"UTF-8"));
for (String number : numbers) {
writer.write(number);
writer.newLine();
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.test包
该包完成菜单及图形化界面的建设以及测试类
1)GUI
package test;
import javax.swing.*;
import Admin.Admin;
import Admin.Rolling;
import lotterySystem.Show;
import lotterySystem.Winning;
import record.DataStorage;
import record.NumbersRecord;
import user.NumberSelection;
import user.User;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
public class GUI {
private JFrame frame;
private JButton loginButton;
private JButton registerButton;
private JButton rechargeButton;
private JButton infoButton;
private JButton playButton;
private JButton adminButton;
private JLabel balanceLabel;
private User currentUser;
static List<String> userRecords = DataStorage.readUserRecrods();
static List<String> numbers ;//放已买的号
public GUI() {
frame = new JFrame("彩票抽奖系统");
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(5, 1));
loginButton = new JButton("登录");
registerButton = new JButton("注册");
rechargeButton = new JButton("充值");
playButton = new JButton("选择号码");
infoButton=new JButton("查看号码");
adminButton = new JButton("管理员");
balanceLabel = new JLabel("余额:0");
buttonPanel.add(loginButton);
buttonPanel.add(registerButton);
buttonPanel.add(rechargeButton);
buttonPanel.add(playButton);
buttonPanel.add(infoButton);
buttonPanel.add(adminButton);
frame.add(buttonPanel, BorderLayout.CENTER);
frame.add(balanceLabel, BorderLayout.SOUTH);
// 登录按钮点击事件
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
userRecords = DataStorage.readUserRecrods();
String username = JOptionPane.showInputDialog("请输入用户名");
String password = JOptionPane.showInputDialog("请输入密码");
currentUser=DataStorage.loginUser(username, password,userRecords);
if (currentUser != null) {
JOptionPane.showMessageDialog(frame, "登录成功!");
balanceLabel.setText("余额:" + currentUser.getBalance());
numbers = NumbersRecord.fileToList(currentUser.getUsername());
Winning.notifyFirstLogin(numbers, currentUser,userRecords,balanceLabel);
}else {
JOptionPane.showMessageDialog(frame, "登录失败!请检查用户名和密码。");
}
}
});
// 注册按钮点击事件
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String username = JOptionPane.showInputDialog("请输入用户名");
String password = JOptionPane.showInputDialog("请输入密码");
String telnumber = JOptionPane.showInputDialog("请输入电话号码");
int r= DataStorage.register(username, password,telnumber,userRecords);
if (r==0) {
JOptionPane.showMessageDialog(frame, "该用户名已被注册,请选择其他用户名。");
} else if (r==-1) {
JOptionPane.showMessageDialog(frame, "密码必须为6位,请重新输入。");
} else if(r==1){
JOptionPane.showMessageDialog(frame, "注册成功!");
}
}
});
// 充值按钮点击事件
rechargeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (currentUser == null) {
JOptionPane.showMessageDialog(frame, "请先登录");
return;
}
String amountStr = JOptionPane.showInputDialog("请输入充值金额");
double amount = Double.parseDouble(amountStr);
currentUser.recharge(currentUser, amount,userRecords);
balanceLabel.setText("余额:" + currentUser.getBalance());
JOptionPane.showMessageDialog(frame, "充值成功,当前余额为:"+currentUser.getBalance());
}
});
// 选择号码按钮点击事件
playButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 实现选择号码逻辑
if (currentUser == null) {
JOptionPane.showMessageDialog(frame, "请先登录");
return;
}
String numberOfBets = JOptionPane.showInputDialog("投注数:");
int bets = Integer.parseInt(numberOfBets);
List<String> newNumbers=new ArrayList<>();
while(bets!=0) {
String[] options = {"手动选号", "随机选号"};
int choice = JOptionPane.showOptionDialog(null, "请选择选号方式", "选号",
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE,
null, options, options[0]);
String number =null;
switch (choice) {
case 0: // 手动选号
number= JOptionPane.showInputDialog("请输入号码");
break;
case 1: // 随机选号
number=NumberSelection.selectNumberRandomly();
break;
default:
break;
}
if(NumberSelection.selectNumber(currentUser, number,numbers)!=null) {
newNumbers.add(number);
bets--;
balanceLabel.setText("余额:" + currentUser.getBalance());
if(currentUser.getBalance() < 2.0&&bets!=0) {
JOptionPane.showMessageDialog(null, "余额不足,请先充值");
break;
}
}else{
break;
}
}
//把新买的彩票保存到文件里,并更新numbers列表
NumbersRecord.addNumberstoList(numbers, newNumbers);
NumbersRecord.saveNumbersToFile(currentUser.getUsername(), newNumbers);
DataStorage.updateBalance(currentUser.getUsername(), currentUser.getBalance(),userRecords) ;
}
});
//查看号码按钮点击事件
infoButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (currentUser == null) {
JOptionPane.showMessageDialog(frame, "请先登录");
return;
}
Show.displayNumbers(numbers, currentUser);
}
});
// 管理员按钮点击事件
adminButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 弹出输入对话框,让管理员输入和密码
String adminName = JOptionPane.showInputDialog(frame, "请输入管理员用户名:");
String adminPassword = JOptionPane.showInputDialog(frame, "请输入管理员密码:");
// 验证管理员登录
if (Admin.validateAdmin(adminName, adminPassword)) {
JOptionPane.showMessageDialog(frame, "管理员登录成功!");
// 登录成功,弹出选择操作对话框
boolean set=false;
while(!set) {
String[] options = {"手动设置中奖号码", "自动设置中奖号码"};
int selectedOption = JOptionPane.showOptionDialog(frame, "请选择管理员操作:", "管理员操作",
JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
String winningNumber =null;
switch (selectedOption) {
case 0: // 手动设置中奖号码
winningNumber = JOptionPane.showInputDialog(frame, "请输入中奖号码:");
break;
case 1:
//滚动抽奖
Rolling.createJframe(userRecords);
// Rolling.rolling(new Rolling.RollingCallback() {
// @Override
// public void onRollingFinished(String winningNumber) {
// JOptionPane.showMessageDialog(frame, "抽奖结束,中奖号码:"+ winningNumber);
// // 继续执行其他逻辑...
// Show.displayWinnerInfos(Winning.checkWinning(userRecords, winningNumber),winningNumber);
// }
// });
// SwingUtilities.invokeLater(() -> new Lottery());
break;
default:
break;
}
if(winningNumber!=null){
set=Rolling.setWinningNumber(winningNumber);
Show.displayWinnerInfos(Winning.checkWinning(userRecords, winningNumber),winningNumber);
}else{
set=true;
}
}
} else {
JOptionPane.showMessageDialog(frame, "管理员用户名或密码错误!");
}
}
});
frame.setVisible(true);
}
}
2)Test
package test;
public class Test {
public static void main(String[] args) {
new GUI();
}
}
5.User包
该包用于用户选号及用户信息的管理
1)NumberSelection类
a. selectNumberRandomly 用户随机选数
b. selectNumber 用户手动选数
package user;
import java.util.List;
import java.util.Random;
import javax.swing.JOptionPane;
public class NumberSelection {
public static String selectNumberRandomly() {
/*该方法用于生成一个随机的七位数字符串,
每位数的范围是0到35
(共36个数字,包括0-9和A-Z,共36个字符*/
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 7; i++) {
int number = random.nextInt(36) + 1;
sb.append(String.format("%02d", number)); // 使用两位宽度格式化数字,并在前面补0
}
return sb.toString();
}
public static String selectNumber(User user, String number, List<String> numbers) {
//该方法的主要目的是检查用户是否有足够的余额来购买彩票,
// 以及所选择的号码是否合法且未被使用过。
double price = 2.0; // 彩票价格
if (user.getBalance() < price) {
JOptionPane.showMessageDialog(null, "余额不足,请先充值");
return null;
}
if (!isValidNumber(number)) {
JOptionPane.showMessageDialog(null, "号码不合法,请重新选择");
return null;
}
if (numbers.contains(number)) {
JOptionPane.showMessageDialog(null, "号码重复,请重新选择");
return null;
}
user.setBalance(user.getBalance() - price); // 扣除购买彩票的金额
JOptionPane.showMessageDialog(null, "选择号码成功,号码为:" + number);
return number;
}
public static boolean isValidNumber(String number) {
//验证一个可能是彩票号码的字符串
if(!number.matches("\\d{14}"))
return false;
for (int i = 0; i < number.length(); i += 2) {
String subNum = number.substring(i, i + 2);
int numValue = Integer.parseInt(subNum);
if (numValue < 1 || numValue > 36) {
return false;
}
}
return true;
}
}
2)User类
package user;
import java.util.List;
import record.DataStorage;
public class User {
private String username;
private String password;
private double balance;
private String telnumber;
// 第一个构造函数:只包含用户名、密码和电话号码,默认余额为 0.0。
//第二个构造函数:包含用户名、密码、余额和电话号码。
//第三个构造函数:包含用户名、密码和余额。
public User(String username, String password,String telnumber) {
this.username=username;
this.password=password;
this.telnumber=telnumber;
this.balance=0.0;
}
public User(String username, String password,double balance,String telnumber) {
this.username=username;
this.password=password;
this.telnumber=telnumber;
this.balance=balance;
}
public User(String username, String password,double balance) {
this.username=username;
this.password=password;
this.balance=balance;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTelnumber() {
return telnumber;
}
public void setTelnumber(String telnumber) {
this.telnumber = telnumber;
}
//recharge`方法用于给用户充值:
//增加用户的余额。
//调用`Datastorage`类的、updateBalance`方法,更新用户的余额信息到存储中。
//输出充值成功的消息和当前余额。
public void recharge(User user, double amount,List<String> userRecords) {
user.setBalance(user.getBalance() + amount);
DataStorage.updateBalance(user.getUsername(), user.getBalance(),userRecords);
System.out.println("充值成功!当前余额为:" + user.getBalance());
}
}
(二)代码运行结果展示
1.主页面
2.登录
3.输入密码
4.进入抽奖平台
5.填写充值数值
6.输入投注数
7.选择选号方式
8.确定选号成功
9.查看中奖情况