JAVA课程设计——彩票购买抽奖系统

一.小组成员及分工

成员名完成部分备注
刘亚楠图形化界面,统计并显示获奖情况,测试代码,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);
            // 检查这个整数是否在136的范围内
            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 -> {
            // 设置rollingtrue,启动滚动
            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,使用datacolumnNames初始化
        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实例,并使用datacolumnNames初始化
        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() {
        /*该方法用于生成一个随机的七位数字符串,
每位数的范围是035
(共36个数字,包括0-9A-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.查看中奖情况

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值