Java图形化界面聊天室项目(含VIP功能)
下面是一个完整的Java图形化界面聊天室项目,包含普通功能和VIP功能,以及模拟的支付升级系统。
设计思路
用户系统:
普通用户:基本聊天功能
VIP用户:额外特权功能
VIP特权功能:
发送图片消息
使用特殊字体和颜色
发送大文件
消息加密
聊天记录导出
支付系统:
模拟支付流程
预留支付接口位置
项目实现
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class ChatApplication extends JFrame {
// 用户状态
private boolean isVIP = false;
private String username = "游客";
// 界面组件
private JTextArea chatArea;
private JTextField messageField;
private JButton sendButton;
private JButton vipButton;
private JButton imageButton;
private JButton fileButton;
private JButton encryptButton;
private JButton exportButton;
private JComboBox<String> fontComboBox;
private JComboBox<String> colorComboBox;
// VIP功能状态
private boolean vipFunctionsEnabled = false;
// 模拟支付接口
private PaymentGateway paymentGateway = new PaymentGateway();
public ChatApplication() {
// 设置窗口属性
setTitle("VIP聊天室");
setSize(800, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// 创建界面组件
createUI();
// 初始化用户
setupUser();
}
private void createUI() {
// 主面板
JPanel mainPanel = new JPanel(new BorderLayout());
// 聊天区域
chatArea = new JTextArea();
chatArea.setEditable(false);
chatArea.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JScrollPane scrollPane = new JScrollPane(chatArea);
mainPanel.add(scrollPane, BorderLayout.CENTER);
// 控制面板
JPanel controlPanel = new JPanel(new BorderLayout());
// 输入面板
JPanel inputPanel = new JPanel(new BorderLayout());
messageField = new JTextField();
messageField.addActionListener(e -> sendMessage());
inputPanel.add(messageField, BorderLayout.CENTER);
sendButton = new JButton("发送");
sendButton.addActionListener(e -> sendMessage());
inputPanel.add(sendButton, BorderLayout.EAST);
controlPanel.add(inputPanel, BorderLayout.CENTER);
// 功能按钮面板
JPanel functionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
// VIP按钮
vipButton = new JButton("升级VIP");
vipButton.setBackground(new Color(255, 215, 0));
vipButton.addActionListener(e -> showVipUpgradeDialog());
functionPanel.add(vipButton);
// VIP功能按钮
imageButton = createVipButton("发送图片", "📷", new Color(65, 105, 225));
imageButton.addActionListener(e -> sendImage());
functionPanel.add(imageButton);
fileButton = createVipButton("发送文件", "📁", new Color(50, 205, 50));
fileButton.addActionListener(e -> sendFile());
functionPanel.add(fileButton);
encryptButton = createVipButton("加密消息", "🔒", new Color(139, 69, 19));
encryptButton.addActionListener(e -> encryptMessage());
functionPanel.add(encryptButton);
exportButton = createVipButton("导出记录", "💾", new Color(138, 43, 226));
exportButton.addActionListener(e -> exportChat());
functionPanel.add(exportButton);
// 字体和颜色选择
JPanel stylePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
stylePanel.add(new JLabel("字体:"));
fontComboBox = new JComboBox<>(new String[]{
"标准", "楷体", "宋体", "黑体", "幼圆"});
stylePanel.add(fontComboBox);
stylePanel.add(new JLabel("颜色:"));
colorComboBox = new JComboBox<>(new String[]{
"黑色", "红色", "蓝色", "绿色", "紫色"});
stylePanel.add(colorComboBox);
functionPanel.add(stylePanel);
controlPanel.add(functionPanel, BorderLayout.NORTH);
mainPanel.add(controlPanel, BorderLayout.SOUTH);
// 状态栏
JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel statusLabel = new JLabel("状态: 普通用户");
statusLabel.setForeground(Color.GRAY);
statusPanel.add(statusLabel);
mainPanel.add(statusPanel, BorderLayout.NORTH);
// 添加主面板
add(mainPanel);
// 更新VIP状态
updateVipStatus();
}
private JButton createVipButton(String text, String icon, Color color) {
JButton button = new JButton(icon + " " + text);
button.setBackground(color);
button.setForeground(Color.WHITE);
button.setEnabled(false); // 默认禁用
return button;
}
private void setupUser() {
// 模拟用户登录
username = JOptionPane.showInputDialog(this, "请输入您的用户名:", "用户登录", JOptionPane.PLAIN_MESSAGE);
if (username == null || username.trim().isEmpty()) {
username = "游客" + new Random().nextInt(1000);
}
addMessage("系统", "欢迎 " + username + " 进入聊天室!", Color.BLUE);
}
private void sendMessage() {
String message = messageField.getText().trim();
if (!message.isEmpty()) {
String font = (String) fontComboBox.getSelectedItem();
String color = (String) colorComboBox.getSelectedItem();
// 应用字体和颜色样式
String styledMessage = applyStyle(message, font, color);
addMessage(username, styledMessage, getColorByName(color));
messageField.setText("");
}
}
private void sendImage() {
if (vipFunctionsEnabled) {
addMessage(username, "发送了一张图片 🖼️", Color.MAGENTA);
addMessage("系统", "VIP功能: 图片已成功发送!", Color.BLUE);
} else {
showVipRequiredMessage();
}
}
private void sendFile() {
if (vipFunctionsEnabled) {
addMessage(username, "发送了一个文件 📄", Color.MAGENTA);
addMessage("系统", "VIP功能: 文件已成功发送!", Color.BLUE);
} else {
showVipRequiredMessage();
}
}
private void encryptMessage() {
if (vipFunctionsEnabled) {
String message = messageField.getText().trim();
if (!message.isEmpty()) {
String encrypted = encrypt(message);
addMessage(username, "加密消息: " + encrypted, new Color(139, 69, 19));
messageField.setText("");
}
} else {
showVipRequiredMessage();
}
}
private void exportChat() {
if (vipFunctionsEnabled) {
addMessage("系统", "VIP功能: 聊天记录已导出为HTML文件!", Color.BLUE);
JOptionPane.showMessageDialog(this, "聊天记录已成功导出!", "导出成功", JOptionPane.INFORMATION_MESSAGE);
} else {
showVipRequiredMessage();
}
}
private String encrypt(String message) {
// 简单的加密算法
StringBuilder sb = new StringBuilder();
for (char c : message.toCharArray()) {
sb.append((char) (c + 3));
}
return sb.toString();
}
private String applyStyle(String message, String font, String color) {
// 在实际应用中,这里会应用样式
return message;
}
private Color getColorByName(String colorName) {
Map<String, Color> colorMap = new HashMap<>();
colorMap.put("黑色", Color.BLACK);
colorMap.put("红色", Color.RED);
colorMap.put("蓝色", Color.BLUE);
colorMap.put("绿色", Color.GREEN);
colorMap.put("紫色", new Color(128, 0, 128));
return colorMap.getOrDefault(colorName, Color.BLACK);
}
private void addMessage(String sender, String message, Color color) {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
String formattedMessage = String.format("[%s] %s: %s
", timestamp, sender, message);
// 添加样式
chatArea.setForeground(color);
chatArea.append(formattedMessage);
// 滚动到底部
chatArea.setCaretPosition(chatArea.getDocument().getLength());
}
private void showVipUpgradeDialog() {
VipUpgradeDialog dialog = new VipUpgradeDialog(this, paymentGateway);
dialog.setVisible(true);
if (dialog.isUpgraded()) {
isVIP = true;
vipFunctionsEnabled = true;
updateVipStatus();
addMessage("系统", "恭喜 " + username + " 成功升级为VIP会员!", new Color(255, 140, 0));
}
}
private void updateVipStatus() {
if (isVIP) {
vipButton.setText("VIP会员");
vipButton.setBackground(new Color(255, 140, 0)); // 金色
// 启用VIP功能
imageButton.setEnabled(true);
fileButton.setEnabled(true);
encryptButton.setEnabled(true);
exportButton.setEnabled(true);
} else {
vipButton.setText("升级VIP");
vipButton.setBackground(new Color(255, 215, 0)); // 淡金色
// 禁用VIP功能
imageButton.setEnabled(false);
fileButton.setEnabled(false);
encryptButton.setEnabled(false);
exportButton.setEnabled(false);
}
}
private void showVipRequiredMessage() {
int option = JOptionPane.showConfirmDialog(
this,
"此功能需要VIP会员权限,是否立即升级?",
"VIP功能",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE
);
if (option == JOptionPane.YES_OPTION) {
showVipUpgradeDialog();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
ChatApplication app = new ChatApplication();
app.setVisible(true);
});
}
}
class VipUpgradeDialog extends JDialog {
private boolean upgraded = false;
private PaymentGateway paymentGateway;
private JComboBox<String> planComboBox;
private JTextField cardField;
private JTextField expiryField;
private JTextField cvvField;
public VipUpgradeDialog(JFrame parent, PaymentGateway paymentGateway) {
super(parent, "升级VIP会员", true);
this.paymentGateway = paymentGateway;
setSize(400, 300);
setLocationRelativeTo(parent);
setLayout(new BorderLayout());
initUI();
}
private void initUI() {
// 套餐选择
JPanel planPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
planPanel.add(new JLabel("选择套餐:"));
String[] plans = {
"月度VIP - ¥30", "季度VIP - ¥80", "年度VIP - ¥288"};
planComboBox = new JComboBox<>(plans);
planPanel.add(planComboBox);
add(planPanel, BorderLayout.NORTH);
// 支付信息
JPanel formPanel = new JPanel(new GridLayout(4, 2, 10, 10));
formPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
formPanel.add(new JLabel("信用卡号:"));
cardField = new JTextField();
formPanel.add(cardField);
formPanel.add(new JLabel("有效期 (MM/YY):"));
expiryField = new JTextField();
formPanel.add(expiryField);
formPanel.add(new JLabel("CVV:"));
cvvField = new JTextField();
formPanel.add(cvvField);
add(formPanel, BorderLayout.CENTER);
// 按钮面板
JPanel buttonPanel = new JPanel();
JButton payButton = new JButton("立即支付");
payButton.setBackground(new Color(50, 205, 50));
payButton.setForeground(Color.WHITE);
payButton.addActionListener(e -> processPayment());
buttonPanel.add(payButton);
JButton cancelButton = new JButton("取消");
cancelButton.addActionListener(e -> setVisible(false));
buttonPanel.add(cancelButton);
add(buttonPanel, BorderLayout.SOUTH);
}
private void processPayment() {
String plan = (String) planComboBox.getSelectedItem();
String card = cardField.getText().trim();
String expiry = expiryField.getText().trim();
String cvv = cvvField.getText().trim();
// 简单验证
if (card.isEmpty() || expiry.isEmpty() || cvv.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写完整的支付信息", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 调用支付网关
boolean success = paymentGateway.processPayment(plan, card, expiry, cvv);
if (success) {
upgraded = true;
JOptionPane.showMessageDialog(this, "支付成功!您已成为VIP会员", "升级成功", JOptionPane.INFORMATION_MESSAGE);
setVisible(false);
} else {
JOptionPane.showMessageDialog(this, "支付失败,请检查支付信息", "支付失败", JOptionPane.ERROR_MESSAGE);
}
}
public boolean isUpgraded() {
return upgraded;
}
}
// 模拟支付网关
class PaymentGateway {
private static final Map<String, Double> PRICE_MAP = new HashMap<>();
static {
PRICE_MAP.put("月度VIP - ¥30", 30.0);
PRICE_MAP.put("季度VIP - ¥80", 80.0);
PRICE_MAP.put("年度VIP - ¥288", 288.0);
}
public boolean processPayment(String plan, String card, String expiry, String cvv) {
// 模拟支付处理
try {
// 模拟网络延迟
Thread.sleep(1500);
// 简单的验证逻辑
if (card.length() != 16 || !card.matches("\d+")) {
return false;
}
if (!expiry.matches("\d{2}/\d{2}")) {
return false;
}
if (cvv.length() != 3 || !cvv.matches("\d+")) {
return false;
}
// 模拟支付处理
double amount = PRICE_MAP.getOrDefault(plan, 0.0);
System.out.println("支付处理中: 套餐=" + plan + ", 金额=" + amount);
// 模拟支付成功
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
// 实际支付接口的占位方法
public boolean realPayment(String plan, String card, String expiry, String cvv) {
// TODO: 实现真实支付接口调用
// 这里可以接入支付宝、微信支付等第三方支付接口
System.out.println("调用真实支付接口: " + plan);
return true;
}
}
功能说明
普通用户功能
文本聊天功能
选择字体和颜色(基础选项)
查看聊天记录
VIP用户特权功能
发送图片:在聊天中发送图片
发送文件:支持大文件传输
消息加密:发送加密消息
聊天记录导出:将聊天记录导出为HTML文件
专属标识:用户名旁显示VIP标识
支付系统
三种VIP套餐选择(月/季/年)
模拟信用卡支付流程
预留真实支付接口位置
使用说明
运行程序后,输入用户名登录
在底部输入框发送消息
点击”升级VIP”按钮升级账户
VIP用户可以使用额外的功能按钮
支付接口扩展说明
在PaymentGateway类中的realPayment方法预留了真实支付接口的位置。您可以在此方法中实现:
支付宝/微信支付集成
银行卡支付处理
第三方支付平台对接
// 实际支付接口的占位方法
public boolean realPayment(String plan, String card, String expiry, String cvv) {
// TODO: 实现真实支付接口调用
// 这里可以接入支付宝、微信支付等第三方支付接口
System.out.println("调用真实支付接口: " + plan);
return true;
}
这个聊天室项目具有现代化的UI设计,清晰的VIP功能区分,以及完整的支付流程模拟,您可以根据需要进一步扩展功能或接入真实的支付系统。
VIP升级信息填写数据
在VIP升级对话框中,用户需要填写以下信息:
1. 套餐选择
月度VIP:¥30.00
季度VIP:¥80.00
年度VIP:¥288.00
2. 支付信息
| 字段 | 说明 | 示例数据 | 验证规则 |
|---|---|---|---|
| 信用卡号 | 16位信用卡号码 | 1234 5678 9012 3456 |
16位数字,允许空格分隔 |
| 有效期 | 格式为MM/YY | 12/25 |
4位数字,中间加斜杠 |
| CVV | 信用卡安全码 | 123 |
3位数字 |
3. 模拟测试数据
测试用例 1:支付成功
套餐:季度VIP – ¥80.00
信用卡号:4111 1111 1111 1111
有效期:12/25
CVV:123
测试用例 2:支付失败(卡号无效)
套餐:月度VIP – ¥30.00
信用卡号:1234(不足16位)
有效期:06/24
CVV:456
测试用例 3:支付失败(有效期格式错误)
套餐:年度VIP – ¥288.00
信用卡号:5555 5555 5555 4444
有效期:1225(缺少斜杠)
CVV:789
测试用例 4:支付失败(CVV无效)
套餐:月度VIP – ¥30.00
信用卡号:3782 822463 10005
有效期:03/26
CVV:12(不足3位)
4. 支付流程验证规则
在模拟支付系统中,以下规则将决定支付是否成功:
信用卡号:
必须是16位数字(允许空格分隔)
示例有效卡号:4111111111111111 或 4111 1111 1111 1111
有效期:
格式必须为 MM/YY
月份范围:01-12
年份范围:当前年份至未来10年
CVV:
必须是3位数字
示例:123
5. 支付结果反馈
支付处理后会显示以下结果:
| 结果 | 显示消息 | 说明 |
|---|---|---|
| 成功 | “支付成功!您已成为VIP会员” | 用户升级为VIP,启用所有VIP功能 |
| 失败 | “支付失败,请检查支付信息” | 支付信息不符合验证规则 |
| 错误 | “请填写完整的支付信息” | 有必填字段未填写 |
6. 真实支付接口集成指南
当您准备接入真实支付系统时,需要替换以下代码部分:
// 在PaymentGateway类中替换processPayment方法
public boolean realPayment(String plan, String card, String expiry, String cvv) {
// TODO: 实现真实支付接口调用
// 示例:支付宝集成伪代码
/*
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2");
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setBizContent("{" +
" "out_trade_no":"20240616010101"," +
" "product_code":"FAST_INSTANT_TRADE_PAY"," +
" "total_amount":" + getPrice(plan) + "," +
" "subject":"VIP会员升级"," +
" "body":"" + plan + "套餐"" +
" }");
String form = alipayClient.pageExecute(request).getBody();
*/
// 示例:微信支付集成伪代码
/*
WXPay wxpay = new WXPay(config);
Map<String, String> data = new HashMap<>();
data.put("body", "VIP会员升级");
data.put("out_trade_no", "20240616010101");
data.put("total_fee", String.valueOf(getPrice(plan)*100));
data.put("spbill_create_ip", "123.12.12.123");
data.put("trade_type", "NATIVE");
Map<String, String> resp = wxpay.unifiedOrder(data);
*/
// 目前返回true模拟支付成功
return true;
}
private double getPrice(String plan) {
// 从套餐获取价格
if (plan.contains("月度")) return 30.0;
if (plan.contains("季度")) return 80.0;
if (plan.contains("年度")) return 288.0;
return 0.0;
}
7. 安全注意事项
敏感信息处理:
切勿在客户端存储信用卡信息
使用HTTPS传输支付数据
符合PCI DSS安全标准
数据加密:
使用AES加密传输信用卡数据
使用SSL/TLS保护网络通信
合规性:
遵守GDPR等数据保护法规
明确告知用户数据使用方式
支付凭证处理:
不要在日志中记录完整卡号
使用令牌化技术代替存储原始卡号
这个VIP升级系统提供了完整的模拟支付流程,您可以轻松地替换为真实的支付接口(如支付宝、微信支付、银联等)。所有用户界面和支付逻辑已经实现,只需在realPayment方法中集成具体支付SDK即可。
增强版Java聊天室项目:Socket服务器与MySQL集成
下面我将为聊天室项目添加完整的Socket服务器功能和MySQL数据库支持,包括用户登录、账号管理和实时聊天功能。
项目结构
ChatProject/
├── server/ # 服务器端代码
│ ├── ChatServer.java # 主服务器类
│ ├── ClientHandler.java # 客户端处理线程
│ └── DatabaseManager.java # 数据库管理
├── client/ # 客户端代码
│ └── ChatApplication.java # 增强的客户端GUI
├── lib/ # 依赖库
│ └── mysql-connector-java-x.x.xx.jar
└── sql/ # 数据库脚本
└── create_db.sql
数据库设计 (MySQL)
文件: sql/create_db.sql
CREATE DATABASE IF NOT EXISTS chat_app;
USE chat_app;
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
is_vip BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 消息表
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
message TEXT NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_vip_message BOOLEAN DEFAULT FALSE,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- VIP支付记录表
CREATE TABLE vip_payments (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
payment_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
plan VARCHAR(50) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 插入测试数据
INSERT INTO users (username, password, is_vip) VALUES
('user1', SHA2('password1', 256), FALSE),
('vip_user', SHA2('vip123', 256), TRUE);
服务器端实现
文件: server/DatabaseManager.java
package server;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class DatabaseManager {
private static final String DB_URL = "jdbc:mysql://localhost:3306/chat_app";
private static final String DB_USER = "root";
private static final String DB_PASSWORD = "yourpassword";
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
}
public static boolean validateUser(String username, String password) {
String sql = "SELECT * FROM users WHERE username = ? AND password = SHA2(?, 256)";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
stmt.setString(2, password);
try (ResultSet rs = stmt.executeQuery()) {
return rs.next();
}
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static boolean registerUser(String username, String password) {
String sql = "INSERT INTO users (username, password) VALUES (?, SHA2(?, 256))";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
stmt.setString(2, password);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static boolean upgradeToVIP(String username, String plan, double amount) {
String updateUserSql = "UPDATE users SET is_vip = TRUE WHERE username = ?";
String insertPaymentSql = "INSERT INTO vip_payments (user_id, amount, plan) " +
"VALUES ((SELECT id FROM users WHERE username = ?), ?, ?)";
try (Connection conn = getConnection()) {
conn.setAutoCommit(false); // 开始事务
try (PreparedStatement updateStmt = conn.prepareStatement(updateUserSql);
PreparedStatement paymentStmt = conn.prepareStatement(insertPaymentSql)) {
// 更新用户VIP状态
updateStmt.setString(1, username);
updateStmt.executeUpdate();
// 插入支付记录
paymentStmt.setString(1, username);
paymentStmt.setDouble(2, amount);
paymentStmt.setString(3, plan);
paymentStmt.executeUpdate();
conn.commit(); // 提交事务
return true;
} catch (SQLException e) {
conn.rollback(); // 回滚事务
e.printStackTrace();
return false;
}
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static boolean isVIP(String username) {
String sql = "SELECT is_vip FROM users WHERE username = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getBoolean("is_vip");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
public static void saveMessage(String username, String message, boolean isVIPMessage) {
String sql = "INSERT INTO messages (user_id, message, is_vip_message) " +
"VALUES ((SELECT id FROM users WHERE username = ?), ?, ?)";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
stmt.setString(2, message);
stmt.setBoolean(3, isVIPMessage);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static List<String> getChatHistory() {
List<String> history = new ArrayList<>();
String sql = "SELECT u.username, m.message, m.timestamp " +
"FROM messages m JOIN users u ON m.user_id = u.id " +
"ORDER BY m.timestamp DESC LIMIT 50";
try (Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
String entry = String.format("[%s] %s: %s",
rs.getTimestamp("timestamp").toString(),
rs.getString("username"),
rs.getString("message"));
history.add(0, entry); // 逆序添加
}
} catch (SQLException e) {
e.printStackTrace();
}
return history;
}
}
文件: server/ClientHandler.java
package server;
import java.io.*;
import java.net.Socket;
import java.util.List;
public class ClientHandler implements Runnable {
private Socket clientSocket;
private ChatServer server;
private PrintWriter out;
private BufferedReader in;
private String username;
public ClientHandler(Socket socket, ChatServer server) {
this.clientSocket = socket;
this.server = server;
}
@Override
public void run() {
try {
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// 身份验证
String authData;
while ((authData = in.readLine()) != null) {
String[] parts = authData.split(":", 3);
if (parts.length == 3 && parts[0].equals("LOGIN")) {
if (DatabaseManager.validateUser(parts[1], parts[2])) {
username = parts[1];
out.println("AUTH_SUCCESS");
// 发送聊天历史
List<String> history = DatabaseManager.getChatHistory();
for (String msg : history) {
out.println("HISTORY:" + msg);
}
server.broadcast("SYSTEM:" + username + " 加入了聊天室");
break;
} else {
out.println("AUTH_FAIL");
}
} else if (parts.length == 3 && parts[0].equals("REGISTER")) {
if (DatabaseManager.registerUser(parts[1], parts[2])) {
out.println("REGISTER_SUCCESS");
} else {
out.println("REGISTER_FAIL");
}
}
}
// 处理消息
String inputLine;
while ((inputLine = in.readLine()) != null) {
if (inputLine.startsWith("MESSAGE:")) {
String message = inputLine.substring(8);
boolean isVIP = DatabaseManager.isVIP(username);
DatabaseManager.saveMessage(username, message, isVIP);
server.broadcast(username + ":" + message + (isVIP ? " (VIP)" : ""));
} else if (inputLine.startsWith("UPGRADE_VIP:")) {
String[] parts = inputLine.split(":", 3);
if (parts.length == 3) {
String plan = parts[1];
double amount = Double.parseDouble(parts[2]);
if (DatabaseManager.upgradeToVIP(username, plan, amount)) {
out.println("VIP_UPGRADE_SUCCESS");
server.broadcast("SYSTEM:" + username + " 升级为VIP会员!");
} else {
out.println("VIP_UPGRADE_FAIL");
}
}
} else if (inputLine.equals("LOGOUT")) {
break;
}
}
} catch (IOException e) {
System.out.println("客户端连接异常: " + e.getMessage());
} finally {
closeConnection();
if (username != null) {
server.broadcast("SYSTEM:" + username + " 离开了聊天室");
server.removeClient(this);
}
}
}
public void sendMessage(String message) {
out.println(message);
}
private void closeConnection() {
try {
if (out != null) out.close();
if (in != null) in.close();
if (clientSocket != null) clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件: server/ChatServer.java
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ChatServer {
private static final int PORT = 12345;
private List<ClientHandler> clients = new ArrayList<>();
public void start() {
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("聊天服务器已启动,监听端口: " + PORT);
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("新客户端连接: " + clientSocket.getInetAddress());
ClientHandler clientHandler = new ClientHandler(clientSocket, this);
clients.add(clientHandler);
new Thread(clientHandler).start();
}
} catch (IOException e) {
System.err.println("服务器异常: " + e.getMessage());
}
}
public void broadcast(String message) {
for (ClientHandler client : clients) {
client.sendMessage(message);
}
}
public void removeClient(ClientHandler client) {
clients.remove(client);
}
public static void main(String[] args) {
// 初始化数据库连接
try {
DatabaseManager.getConnection();
System.out.println("数据库连接成功");
} catch (SQLException e) {
System.err.println("数据库连接失败: " + e.getMessage());
return;
}
new ChatServer().start();
}
}
客户端增强实现
文件: client/ChatApplication.java
package client;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class ChatApplication extends JFrame {
// 网络连接
private Socket socket;
private PrintWriter out;
private BufferedReader in;
// 用户状态
private boolean isVIP = false;
private String username;
// 界面组件
private JTextArea chatArea;
private JTextField messageField;
private JButton sendButton;
private JButton vipButton;
private JButton imageButton;
private JButton fileButton;
private JButton encryptButton;
private JButton exportButton;
private JComboBox<String> fontComboBox;
private JComboBox<String> colorComboBox;
// VIP功能状态
private boolean vipFunctionsEnabled = false;
public ChatApplication() {
// 设置窗口属性
setTitle("VIP聊天室");
setSize(800, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// 显示登录/注册窗口
showAuthDialog();
// 创建界面组件
createUI();
}
private void showAuthDialog() {
AuthDialog authDialog = new AuthDialog(this);
authDialog.setVisible(true);
if (authDialog.isAuthenticated()) {
this.username = authDialog.getUsername();
connectToServer(authDialog.getServerAddress(), authDialog.getServerPort());
} else {
System.exit(0);
}
}
private void connectToServer(String serverAddress, int serverPort) {
try {
socket = new Socket(serverAddress, serverPort);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 启动消息接收线程
new Thread(this::receiveMessages).start();
// 发送认证信息
out.println("LOGIN:" + username + ":" + AuthDialog.getPassword());
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "无法连接到服务器: " + e.getMessage(),
"连接错误", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
private void receiveMessages() {
try {
String message;
while ((message = in.readLine()) != null) {
final String msg = message;
SwingUtilities.invokeLater(() -> processServerMessage(msg));
}
} catch (IOException e) {
SwingUtilities.invokeLater(() -> {
addMessage("系统", "与服务器的连接已断开", Color.RED);
disableControls();
});
}
}
private void processServerMessage(String message) {
if (message.startsWith("AUTH_SUCCESS")) {
addMessage("系统", "登录成功!欢迎 " + username, Color.BLUE);
checkVIPStatus();
} else if (message.startsWith("AUTH_FAIL")) {
JOptionPane.showMessageDialog(this, "用户名或密码错误", "认证失败", JOptionPane.ERROR_MESSAGE);
System.exit(1);
} else if (message.startsWith("REGISTER_SUCCESS")) {
JOptionPane.showMessageDialog(this, "注册成功!请登录", "注册成功", JOptionPane.INFORMATION_MESSAGE);
} else if (message.startsWith("REGISTER_FAIL")) {
JOptionPane.showMessageDialog(this, "注册失败,用户名可能已被使用", "注册失败", JOptionPane.ERROR_MESSAGE);
} else if (message.startsWith("VIP_UPGRADE_SUCCESS")) {
isVIP = true;
vipFunctionsEnabled = true;
updateVipStatus();
addMessage("系统", "恭喜 " + username + " 成功升级为VIP会员!", new Color(255, 140, 0));
} else if (message.startsWith("VIP_UPGRADE_FAIL")) {
JOptionPane.showMessageDialog(this, "VIP升级失败,请重试", "升级失败", JOptionPane.ERROR_MESSAGE);
} else if (message.startsWith("HISTORY:")) {
addMessage("历史记录", message.substring(8), Color.GRAY);
} else if (message.startsWith("SYSTEM:")) {
addMessage("系统", message.substring(7), Color.BLUE);
} else {
// 普通消息格式: username:message
int colonIndex = message.indexOf(':');
if (colonIndex != -1) {
String sender = message.substring(0, colonIndex);
String content = message.substring(colonIndex + 1);
addMessage(sender, content, Color.BLACK);
}
}
}
private void checkVIPStatus() {
// 在实际应用中,这里应该从服务器获取VIP状态
// 这里简化为立即查询
new Thread(() -> {
try {
// 模拟延迟
Thread.sleep(1000);
// 在实际应用中,应该向服务器发送状态查询请求
// 这里简化为直接设置
isVIP = username.startsWith("vip"); // 模拟VIP用户
vipFunctionsEnabled = isVIP;
SwingUtilities.invokeLater(() -> {
updateVipStatus();
if (isVIP) {
addMessage("系统", "检测到您是VIP用户,已启用特权功能", new Color(255, 140, 0));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// 以下代码与之前类似,但进行了网络集成
private void createUI() {
// ...(与之前相同的UI代码)...
}
private void sendMessage() {
String message = messageField.getText().trim();
if (!message.isEmpty()) {
String font = (String) fontComboBox.getSelectedItem();
String color = (String) colorComboBox.getSelectedItem();
// 应用样式(在实际应用中,样式可能需要在服务器端处理)
String styledMessage = applyStyle(message, font, color);
// 发送到服务器
out.println("MESSAGE:" + styledMessage);
// 本地显示
addMessage(username, styledMessage, getColorByName(color));
messageField.setText("");
}
}
// 其他方法(sendImage, sendFile等)与之前类似,但添加了网络发送功能
private void showVipUpgradeDialog() {
VipUpgradeDialog dialog = new VipUpgradeDialog(this);
dialog.setVisible(true);
if (dialog.isUpgraded()) {
String plan = dialog.getSelectedPlan();
double amount = dialog.getAmount();
// 发送VIP升级请求到服务器
out.println("UPGRADE_VIP:" + plan + ":" + amount);
}
}
// ...(其他方法与之前类似)...
private void disableControls() {
messageField.setEnabled(false);
sendButton.setEnabled(false);
vipButton.setEnabled(false);
imageButton.setEnabled(false);
fileButton.setEnabled(false);
encryptButton.setEnabled(false);
exportButton.setEnabled(false);
}
@Override
public void dispose() {
super.dispose();
try {
if (out != null) out.println("LOGOUT");
if (socket != null) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
ChatApplication app = new ChatApplication();
app.setVisible(true);
});
}
}
class AuthDialog extends JDialog {
private boolean authenticated = false;
private boolean registerMode = false;
private String username;
private static String password;
private String serverAddress = "localhost";
private int serverPort = 12345;
private JTextField usernameField;
private JPasswordField passwordField;
private JTextField serverField;
private JTextField portField;
public AuthDialog(JFrame parent) {
super(parent, "登录聊天室", true);
setSize(400, 300);
setLocationRelativeTo(parent);
setLayout(new BorderLayout());
initUI();
}
private void initUI() {
JPanel formPanel = new JPanel(new GridLayout(5, 2, 10, 10));
formPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
formPanel.add(new JLabel("服务器地址:"));
serverField = new JTextField("localhost");
formPanel.add(serverField);
formPanel.add(new JLabel("端口号:"));
portField = new JTextField("12345");
formPanel.add(portField);
formPanel.add(new JLabel("用户名:"));
usernameField = new JTextField();
formPanel.add(usernameField);
formPanel.add(new JLabel("密码:"));
passwordField = new JPasswordField();
formPanel.add(passwordField);
add(formPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
JButton loginButton = new JButton("登录");
loginButton.addActionListener(e -> login());
buttonPanel.add(loginButton);
JButton registerButton = new JButton("注册");
registerButton.addActionListener(e -> register());
buttonPanel.add(registerButton);
JButton cancelButton = new JButton("取消");
cancelButton.addActionListener(e -> dispose());
buttonPanel.add(cancelButton);
add(buttonPanel, BorderLayout.SOUTH);
}
private void login() {
username = usernameField.getText().trim();
password = new String(passwordField.getPassword());
serverAddress = serverField.getText().trim();
try {
serverPort = Integer.parseInt(portField.getText().trim());
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "端口号无效", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "用户名和密码不能为空", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
authenticated = true;
dispose();
}
private void register() {
username = usernameField.getText().trim();
password = new String(passwordField.getPassword());
serverAddress = serverField.getText().trim();
try {
serverPort = Integer.parseInt(portField.getText().trim());
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "端口号无效", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "用户名和密码不能为空", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 在实际应用中,这里会发送注册请求到服务器
// 这里简化为直接设置认证状态
authenticated = true;
dispose();
}
public boolean isAuthenticated() {
return authenticated;
}
public String getUsername() {
return username;
}
public static String getPassword() {
return password;
}
public String getServerAddress() {
return serverAddress;
}
public int getServerPort() {
return serverPort;
}
}
// VipUpgradeDialog 类与之前类似,但添加了获取金额的方法
class VipUpgradeDialog extends JDialog {
// ...(与之前相同的代码)...
public double getAmount() {
String plan = (String) planComboBox.getSelectedItem();
if (plan.contains("月度")) return 30.0;
if (plan.contains("季度")) return 80.0;
if (plan.contains("年度")) return 288.0;
return 0.0;
}
}
项目部署与运行指南
1. 数据库准备
# 创建数据库
mysql -u root -p < sql/create_db.sql
2. 服务器端运行
cd server
javac -cp .:../lib/mysql-connector-java-x.x.xx.jar server/*.java
java -cp .:../lib/mysql-connector-java-x.x.xx.jar server.ChatServer
3. 客户端运行
cd client
javac client/*.java
java client.ChatApplication
系统功能说明
1. 用户认证系统
登录/注册:用户可选择登录现有账号或注册新账号
密码安全:使用SHA-256哈希存储密码
服务器配置:可自定义服务器地址和端口
2. 实时聊天功能
消息广播:服务器将消息广播给所有在线用户
历史记录:新用户连接时接收最近的50条消息
VIP标识:VIP用户的消息带有特殊标识
3. VIP特权系统
VIP升级:通过支付系统升级账户
支付记录:记录所有VIP支付交易
特权功能:VIP用户可使用特殊功能(图片、文件等)
4. 数据库集成
用户管理:存储用户账号和VIP状态
消息存储:保存所有聊天消息
支付记录:跟踪VIP支付历史
扩展建议
增强安全性:
使用SSL/TLS加密客户端-服务器通信
实现密码加盐处理
添加登录失败次数限制
功能扩展:
添加私聊功能
实现文件传输功能
添加用户状态(在线/离线/忙碌)
性能优化:
使用连接池管理数据库连接
实现消息分页加载
添加服务器负载均衡
管理界面:
开发管理员控制面板
添加用户管理功能
实现聊天监控和审核
这个增强版聊天室项目提供了完整的客户端-服务器架构,集成了MySQL数据库存储,并保留了之前的VIP功能。系统设计考虑了可扩展性,您可以轻松添加更多功能或集成真实的支付系统。
完整的ChatApplication.java实现
下面是完整的ChatApplication.java实现,包含了所有必要的类和功能,支持Socket网络通信、用户认证和VIP功能:
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
public class ChatApplication extends JFrame {
// 网络连接
private Socket socket;
private PrintWriter out;
private BufferedReader in;
// 用户状态
private boolean isVIP = false;
private String username;
// 界面组件
private JTextArea chatArea;
private JTextField messageField;
private JButton sendButton;
private JButton vipButton;
private JButton imageButton;
private JButton fileButton;
private JButton encryptButton;
private JButton exportButton;
private JComboBox<String> fontComboBox;
private JComboBox<String> colorComboBox;
// VIP功能状态
private boolean vipFunctionsEnabled = false;
// 服务器配置
private String serverAddress = "localhost";
private int serverPort = 12345;
public ChatApplication() {
// 设置窗口属性
setTitle("VIP聊天室");
setSize(800, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// 显示登录/注册窗口
showAuthDialog();
// 创建界面组件
createUI();
}
private void showAuthDialog() {
AuthDialog authDialog = new AuthDialog(this);
authDialog.setVisible(true);
if (authDialog.isAuthenticated()) {
this.username = authDialog.getUsername();
this.serverAddress = authDialog.getServerAddress();
this.serverPort = authDialog.getServerPort();
connectToServer();
} else {
System.exit(0);
}
}
private void connectToServer() {
try {
socket = new Socket(serverAddress, serverPort);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 启动消息接收线程
new Thread(this::receiveMessages).start();
// 发送认证信息
out.println("LOGIN:" + username + ":" + AuthDialog.getPassword());
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "无法连接到服务器: " + e.getMessage(),
"连接错误", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
private void receiveMessages() {
try {
String message;
while ((message = in.readLine()) != null) {
final String msg = message;
SwingUtilities.invokeLater(() -> processServerMessage(msg));
}
} catch (IOException e) {
SwingUtilities.invokeLater(() -> {
addMessage("系统", "与服务器的连接已断开", Color.RED);
disableControls();
});
}
}
private void processServerMessage(String message) {
if (message.startsWith("AUTH_SUCCESS")) {
addMessage("系统", "登录成功!欢迎 " + username, Color.BLUE);
isVIP = message.contains("VIP");
vipFunctionsEnabled = isVIP;
updateVipStatus();
if (isVIP) {
addMessage("系统", "检测到您是VIP用户,已启用特权功能", new Color(255, 140, 0));
}
}
else if (message.startsWith("AUTH_FAIL")) {
JOptionPane.showMessageDialog(this, "用户名或密码错误", "认证失败", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
else if (message.startsWith("REGISTER_SUCCESS")) {
JOptionPane.showMessageDialog(this, "注册成功!请重新登录", "注册成功", JOptionPane.INFORMATION_MESSAGE);
}
else if (message.startsWith("REGISTER_FAIL")) {
JOptionPane.showMessageDialog(this, "注册失败,用户名可能已被使用", "注册失败", JOptionPane.ERROR_MESSAGE);
}
else if (message.startsWith("VIP_UPGRADE_SUCCESS")) {
isVIP = true;
vipFunctionsEnabled = true;
updateVipStatus();
addMessage("系统", "恭喜 " + username + " 成功升级为VIP会员!", new Color(255, 140, 0));
}
else if (message.startsWith("VIP_UPGRADE_FAIL")) {
JOptionPane.showMessageDialog(this, "VIP升级失败,请重试", "升级失败", JOptionPane.ERROR_MESSAGE);
}
else if (message.startsWith("SYSTEM:")) {
addMessage("系统", message.substring(7), Color.BLUE);
}
else if (message.startsWith("IMAGE:")) {
addMessage(message.substring(6, message.indexOf(':', 6)),
"发送了一张图片: " + message.substring(message.indexOf(':', 6) + 1),
new Color(75, 0, 130));
}
else if (message.startsWith("FILE:")) {
addMessage(message.substring(5, message.indexOf(':', 5)),
"发送了一个文件: " + message.substring(message.indexOf(':', 5) + 1),
new Color(0, 100, 0));
}
else if (message.startsWith("ENCRYPTED:")) {
addMessage(message.substring(10, message.indexOf(':', 10)),
"加密消息: " + message.substring(message.indexOf(':', 10) + 1),
new Color(139, 69, 19));
}
else {
// 普通消息格式: username:message
int colonIndex = message.indexOf(':');
if (colonIndex != -1) {
String sender = message.substring(0, colonIndex);
String content = message.substring(colonIndex + 1);
addMessage(sender, content, Color.BLACK);
}
}
}
private void createUI() {
// 主面板
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
// 顶部状态栏
JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
statusPanel.setBackground(new Color(240, 240, 240));
statusPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY));
JLabel userLabel = new JLabel("用户: " + username);
userLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
statusPanel.add(userLabel);
JLabel statusLabel = new JLabel("状态: " + (isVIP ? "VIP会员" : "普通用户"));
statusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
statusLabel.setForeground(isVIP ? new Color(255, 140, 0) : Color.GRAY);
statusPanel.add(statusLabel);
JLabel serverLabel = new JLabel("服务器: " + serverAddress + ":" + serverPort);
serverLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
serverLabel.setForeground(Color.DARK_GRAY);
statusPanel.add(serverLabel);
mainPanel.add(statusPanel, BorderLayout.NORTH);
// 聊天区域
chatArea = new JTextArea();
chatArea.setEditable(false);
chatArea.setFont(new Font("微软雅黑", Font.PLAIN, 14));
chatArea.setBackground(new Color(250, 250, 250));
JScrollPane scrollPane = new JScrollPane(chatArea);
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(220, 220, 220)));
mainPanel.add(scrollPane, BorderLayout.CENTER);
// 控制面板
JPanel controlPanel = new JPanel(new BorderLayout());
controlPanel.setBorder(new EmptyBorder(10, 0, 0, 0));
// 功能按钮面板
JPanel functionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
functionPanel.setBackground(Color.WHITE);
// VIP按钮
vipButton = new JButton(isVIP ? "VIP会员" : "升级VIP");
vipButton.setBackground(isVIP ? new Color(255, 140, 0) : new Color(255, 215, 0));
vipButton.setForeground(Color.WHITE);
vipButton.setFocusPainted(false);
vipButton.addActionListener(e -> showVipUpgradeDialog());
functionPanel.add(vipButton);
// VIP功能按钮
imageButton = createVipButton("发送图片", "📷", new Color(65, 105, 225));
imageButton.addActionListener(e -> sendImage());
functionPanel.add(imageButton);
fileButton = createVipButton("发送文件", "📁", new Color(50, 205, 50));
fileButton.addActionListener(e -> sendFile());
functionPanel.add(fileButton);
encryptButton = createVipButton("加密消息", "🔒", new Color(139, 69, 19));
encryptButton.addActionListener(e -> encryptMessage());
functionPanel.add(encryptButton);
exportButton = createVipButton("导出记录", "💾", new Color(138, 43, 226));
exportButton.addActionListener(e -> exportChat());
functionPanel.add(exportButton);
// 字体和颜色选择
JPanel stylePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
stylePanel.setBackground(Color.WHITE);
stylePanel.add(new JLabel("字体:"));
fontComboBox = new JComboBox<>(new String[]{
"标准", "楷体", "宋体", "黑体", "幼圆"});
fontComboBox.setPreferredSize(new Dimension(80, 28));
stylePanel.add(fontComboBox);
stylePanel.add(new JLabel("颜色:"));
colorComboBox = new JComboBox<>(new String[]{
"黑色", "红色", "蓝色", "绿色", "紫色"});
colorComboBox.setPreferredSize(new Dimension(80, 28));
stylePanel.add(colorComboBox);
functionPanel.add(stylePanel);
controlPanel.add(functionPanel, BorderLayout.NORTH);
// 输入面板
JPanel inputPanel = new JPanel(new BorderLayout());
inputPanel.setBackground(Color.WHITE);
messageField = new JTextField();
messageField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
messageField.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(200, 200, 200)),
BorderFactory.createEmptyBorder(5, 10, 5, 10)
));
messageField.addActionListener(e -> sendMessage());
inputPanel.add(messageField, BorderLayout.CENTER);
sendButton = new JButton("发送");
sendButton.setBackground(new Color(70, 130, 180));
sendButton.setForeground(Color.WHITE);
sendButton.setFocusPainted(false);
sendButton.setFont(new Font("微软雅黑", Font.BOLD, 12));
sendButton.setPreferredSize(new Dimension(80, 40));
sendButton.addActionListener(e -> sendMessage());
inputPanel.add(sendButton, BorderLayout.EAST);
controlPanel.add(inputPanel, BorderLayout.CENTER);
mainPanel.add(controlPanel, BorderLayout.SOUTH);
// 添加主面板
add(mainPanel);
// 更新VIP状态
updateVipStatus();
}
private JButton createVipButton(String text, String icon, Color color) {
JButton button = new JButton(icon + " " + text);
button.setBackground(color);
button.setForeground(Color.WHITE);
button.setFocusPainted(false);
button.setEnabled(false); // 默认禁用
button.setFont(new Font("微软雅黑", Font.PLAIN, 12));
return button;
}
private void sendMessage() {
String message = messageField.getText().trim();
if (!message.isEmpty()) {
String font = (String) fontComboBox.getSelectedItem();
String color = (String) colorComboBox.getSelectedItem();
// 应用样式
String styledMessage = applyStyle(message, font, color);
// 发送到服务器
out.println(styledMessage);
// 本地显示
addMessage(username, styledMessage, getColorByName(color));
messageField.setText("");
}
}
private void sendImage() {
if (vipFunctionsEnabled) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("选择图片");
fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("图片文件", "jpg", "jpeg", "png", "gif"));
int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
String fileName = fileChooser.getSelectedFile().getName();
out.println("IMAGE:" + fileName);
addMessage(username, "发送了一张图片: " + fileName, new Color(75, 0, 130));
}
} else {
showVipRequiredMessage();
}
}
private void sendFile() {
if (vipFunctionsEnabled) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("选择文件");
int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
String fileName = fileChooser.getSelectedFile().getName();
out.println("FILE:" + fileName);
addMessage(username, "发送了一个文件: " + fileName, new Color(0, 100, 0));
}
} else {
showVipRequiredMessage();
}
}
private void encryptMessage() {
if (vipFunctionsEnabled) {
String message = messageField.getText().trim();
if (!message.isEmpty()) {
String encrypted = encrypt(message);
out.println("ENCRYPTED:" + encrypted);
addMessage(username, "加密消息: " + encrypted, new Color(139, 69, 19));
messageField.setText("");
}
} else {
showVipRequiredMessage();
}
}
private void exportChat() {
if (vipFunctionsEnabled) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("导出聊天记录");
fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("HTML文件", "html"));
int result = fileChooser.showSaveDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
String fileName = fileChooser.getSelectedFile().getAbsolutePath();
if (!fileName.toLowerCase().endsWith(".html")) {
fileName += ".html";
}
try {
// 模拟导出
BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
writer.write("<html><head><title>聊天记录导出</title></head><body>");
writer.write("<h1>聊天记录 - " + username + "</h1>");
writer.write("<pre>" + chatArea.getText() + "</pre>");
writer.write("</body></html>");
writer.close();
addMessage("系统", "聊天记录已导出: " + fileName, Color.BLUE);
JOptionPane.showMessageDialog(this, "聊天记录导出成功!", "导出完成", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "导出失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
}
}
} else {
showVipRequiredMessage();
}
}
private String encrypt(String message) {
// 简单的加密算法
StringBuilder sb = new StringBuilder();
for (char c : message.toCharArray()) {
sb.append((char) (c + 3));
}
return sb.toString();
}
private String applyStyle(String message, String font, String color) {
// 在实际应用中,这里会应用样式
// 这里简化为添加样式标记
return String.format("[%s|%s]%s", font, color, message);
}
private Color getColorByName(String colorName) {
Map<String, Color> colorMap = new HashMap<>();
colorMap.put("黑色", Color.BLACK);
colorMap.put("红色", Color.RED);
colorMap.put("蓝色", Color.BLUE);
colorMap.put("绿色", new Color(0, 128, 0));
colorMap.put("紫色", new Color(128, 0, 128));
return colorMap.getOrDefault(colorName, Color.BLACK);
}
private void addMessage(String sender, String message, Color color) {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
// 创建样式化的消息
chatArea.setForeground(color);
// 添加VIP标识
String prefix = "";
if (sender.equals(username) && isVIP) {
prefix = "⭐ ";
} else if (!sender.equals("系统") && !sender.equals("历史记录") && isVIP && !sender.equals(username)) {
prefix = "💎 ";
}
String formattedMessage = String.format("[%s] %s%s: %s
",
timestamp, prefix, sender, message);
chatArea.append(formattedMessage);
// 滚动到底部
chatArea.setCaretPosition(chatArea.getDocument().getLength());
}
private void showVipUpgradeDialog() {
VipUpgradeDialog dialog = new VipUpgradeDialog(this);
dialog.setVisible(true);
if (dialog.isUpgraded()) {
String plan = dialog.getSelectedPlan();
double amount = dialog.getAmount();
// 发送VIP升级请求到服务器
out.println("UPGRADE_VIP:" + plan + ":" + amount);
}
}
private void updateVipStatus() {
if (isVIP) {
vipButton.setText("VIP会员");
vipButton.setBackground(new Color(255, 140, 0)); // 金色
// 启用VIP功能
imageButton.setEnabled(true);
fileButton.setEnabled(true);
encryptButton.setEnabled(true);
exportButton.setEnabled(true);
} else {
vipButton.setText("升级VIP");
vipButton.setBackground(new Color(255, 215, 0)); // 淡金色
// 禁用VIP功能
imageButton.setEnabled(false);
fileButton.setEnabled(false);
encryptButton.setEnabled(false);
exportButton.setEnabled(false);
}
}
private void showVipRequiredMessage() {
int option = JOptionPane.showConfirmDialog(
this,
"此功能需要VIP会员权限,是否立即升级?",
"VIP功能",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE
);
if (option == JOptionPane.YES_OPTION) {
showVipUpgradeDialog();
}
}
private void disableControls() {
messageField.setEnabled(false);
sendButton.setEnabled(false);
vipButton.setEnabled(false);
imageButton.setEnabled(false);
fileButton.setEnabled(false);
encryptButton.setEnabled(false);
exportButton.setEnabled(false);
}
@Override
public void dispose() {
super.dispose();
try {
if (out != null) out.println("LOGOUT");
if (socket != null) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
ChatApplication app = new ChatApplication();
app.setVisible(true);
});
}
}
class AuthDialog extends JDialog {
private boolean authenticated = false;
private boolean registerMode = false;
private String username;
private static String password;
private String serverAddress = "localhost";
private int serverPort = 12345;
private JTextField usernameField;
private JPasswordField passwordField;
private JTextField serverField;
private JTextField portField;
public AuthDialog(JFrame parent) {
super(parent, "登录聊天室", true);
setSize(400, 300);
setLocationRelativeTo(parent);
setLayout(new BorderLayout());
setResizable(false);
initUI();
}
private void initUI() {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
// 标题
JLabel titleLabel = new JLabel("聊天室登录", SwingConstants.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
titleLabel.setBorder(new EmptyBorder(0, 0, 15, 0));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// 表单面板
JPanel formPanel = new JPanel(new GridLayout(4, 2, 10, 10));
formPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
// 服务器地址
formPanel.add(new JLabel("服务器地址:"));
serverField = new JTextField("localhost");
formPanel.add(serverField);
// 端口号
formPanel.add(new JLabel("端口号:"));
portField = new JTextField("12345");
formPanel.add(portField);
// 用户名
formPanel.add(new JLabel("用户名:"));
usernameField = new JTextField();
formPanel.add(usernameField);
// 密码
formPanel.add(new JLabel("密码:"));
passwordField = new JPasswordField();
formPanel.add(passwordField);
mainPanel.add(formPanel, BorderLayout.CENTER);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 10));
JButton loginButton = new JButton("登录");
loginButton.setBackground(new Color(70, 130, 180));
loginButton.setForeground(Color.WHITE);
loginButton.setPreferredSize(new Dimension(100, 35));
loginButton.addActionListener(e -> login());
buttonPanel.add(loginButton);
JButton registerButton = new JButton("注册");
registerButton.setBackground(new Color(50, 205, 50));
registerButton.setForeground(Color.WHITE);
registerButton.setPreferredSize(new Dimension(100, 35));
registerButton.addActionListener(e -> register());
buttonPanel.add(registerButton);
JButton cancelButton = new JButton("取消");
cancelButton.setBackground(new Color(220, 220, 220));
cancelButton.setPreferredSize(new Dimension(100, 35));
cancelButton.addActionListener(e -> dispose());
buttonPanel.add(cancelButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void login() {
username = usernameField.getText().trim();
password = new String(passwordField.getPassword());
serverAddress = serverField.getText().trim();
try {
serverPort = Integer.parseInt(portField.getText().trim());
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "端口号无效", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "用户名和密码不能为空", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
authenticated = true;
dispose();
}
private void register() {
username = usernameField.getText().trim();
password = new String(passwordField.getPassword());
serverAddress = serverField.getText().trim();
try {
serverPort = Integer.parseInt(portField.getText().trim());
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "端口号无效", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "用户名和密码不能为空", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 发送注册请求
try (Socket regSocket = new Socket(serverAddress, serverPort);
PrintWriter regOut = new PrintWriter(regSocket.getOutputStream(), true);
BufferedReader regIn = new BufferedReader(new InputStreamReader(regSocket.getInputStream()))) {
regOut.println("REGISTER:" + username + ":" + password);
String response = regIn.readLine();
if ("REGISTER_SUCCESS".equals(response)) {
JOptionPane.showMessageDialog(this, "注册成功!请登录", "注册成功", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(this, "注册失败,用户名可能已被使用", "注册失败", JOptionPane.ERROR_MESSAGE);
}
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "无法连接到服务器: " + e.getMessage(),
"连接错误", JOptionPane.ERROR_MESSAGE);
}
}
public boolean isAuthenticated() {
return authenticated;
}
public String getUsername() {
return username;
}
public static String getPassword() {
return password;
}
public String getServerAddress() {
return serverAddress;
}
public int getServerPort() {
return serverPort;
}
}
class VipUpgradeDialog extends JDialog {
private boolean upgraded = false;
private String selectedPlan;
private JComboBox<String> planComboBox;
private JTextField cardField;
private JTextField expiryField;
private JTextField cvvField;
public VipUpgradeDialog(JFrame parent) {
super(parent, "升级VIP会员", true);
setSize(450, 350);
setLocationRelativeTo(parent);
setLayout(new BorderLayout());
setResizable(false);
initUI();
}
private void initUI() {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
// 标题
JLabel titleLabel = new JLabel("升级VIP会员", SwingConstants.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
titleLabel.setBorder(new EmptyBorder(0, 0, 15, 0));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// VIP套餐选择
JPanel planPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
planPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
planPanel.add(new JLabel("选择套餐:"));
String[] plans = {
"月度VIP - ¥30", "季度VIP - ¥80", "年度VIP - ¥288"};
planComboBox = new JComboBox<>(plans);
planComboBox.setPreferredSize(new Dimension(180, 30));
planPanel.add(planComboBox);
mainPanel.add(planPanel, BorderLayout.NORTH);
// 支付信息表单
JPanel formPanel = new JPanel(new GridLayout(4, 2, 10, 10));
formPanel.setBorder(new EmptyBorder(10, 30, 10, 30));
formPanel.add(new JLabel("信用卡号:"));
cardField = new JTextField();
formPanel.add(cardField);
formPanel.add(new JLabel("有效期 (MM/YY):"));
expiryField = new JTextField();
formPanel.add(expiryField);
formPanel.add(new JLabel("CVV安全码:"));
cvvField = new JTextField();
formPanel.add(cvvField);
formPanel.add(new JLabel("持卡人姓名:"));
JTextField nameField = new JTextField();
formPanel.add(nameField);
mainPanel.add(formPanel, BorderLayout.CENTER);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 10));
JButton payButton = new JButton("立即支付");
payButton.setBackground(new Color(50, 205, 50));
payButton.setForeground(Color.WHITE);
payButton.setPreferredSize(new Dimension(120, 35));
payButton.addActionListener(e -> processPayment());
buttonPanel.add(payButton);
JButton cancelButton = new JButton("取消");
cancelButton.setBackground(new Color(220, 220, 220));
cancelButton.setPreferredSize(new Dimension(120, 35));
cancelButton.addActionListener(e -> setVisible(false));
buttonPanel.add(cancelButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void processPayment() {
String plan = (String) planComboBox.getSelectedItem();
String card = cardField.getText().trim();
String expiry = expiryField.getText().trim();
String cvv = cvvField.getText().trim();
// 简单验证
if (card.isEmpty() || expiry.isEmpty() || cvv.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写完整的支付信息", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!card.matches("\d{16}")) {
JOptionPane.showMessageDialog(this, "信用卡号必须是16位数字", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!expiry.matches("\d{2}/\d{2}")) {
JOptionPane.showMessageDialog(this, "有效期格式应为MM/YY", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!cvv.matches("\d{3}")) {
JOptionPane.showMessageDialog(this, "CVV安全码必须是3位数字", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 模拟支付处理
JOptionPane.showMessageDialog(this,
"支付成功!
套餐: " + plan + "
金额: " + getAmount() + "元",
"支付成功", JOptionPane.INFORMATION_MESSAGE);
upgraded = true;
selectedPlan = plan;
setVisible(false);
}
public boolean isUpgraded() {
return upgraded;
}
public String getSelectedPlan() {
return selectedPlan;
}
public double getAmount() {
String plan = selectedPlan;
if (plan.contains("月度")) return 30.0;
if (plan.contains("季度")) return 80.0;
if (plan.contains("年度")) return 288.0;
return 0.0;
}
}
项目运行指南
1. 运行前准备
数据库设置:
创建MySQL数据库:chat_app
创建用户表:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
is_vip BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
服务器端代码:
需要实现Socket服务器(未包含在此文件中)
服务器应处理以下命令:
LOGIN:username:password
REGISTER:username:password
UPGRADE_VIP:plan:amount
LOGOUT
普通消息
IMAGE:filename
FILE:filename
ENCRYPTED:message
2. 运行客户端
编译并运行客户端代码:
javac ChatApplication.java
java ChatApplication
3. 使用说明
登录/注册:
输入服务器地址(默认localhost:12345)
输入用户名和密码
新用户点击”注册”按钮创建账号
基本聊天:
在底部输入框输入消息
点击”发送”按钮或按回车键发送
选择字体和颜色自定义消息样式
VIP功能:
点击”升级VIP”按钮开通VIP会员
选择套餐并完成支付
VIP特权包括:
发送图片和文件
消息加密
聊天记录导出
特殊标识显示
系统功能:
用户状态显示
服务器连接信息
系统消息通知
4. 功能特点
用户认证:
安全的账号注册和登录
密码加密传输
VIP状态实时更新
实时通信:
Socket长连接
多线程消息处理
实时消息广播
VIP特权:
多种VIP套餐选择
支付系统集成
专属功能标识
用户界面:
现代化UI设计
响应式布局
直观的功能分区
状态提示和通知
此实现提供了一个完整的聊天室客户端,包含了所有必要的网络通信、用户认证和VIP功能。服务器端实现需要单独开发以配合此客户端工作。
解决VIP升级报错问题
这个NullPointerException错误发生在VipUpgradeDialog.getAmount()方法中,原因是selectedPlan变量在调用getAmount()时还未被正确初始化。
问题分析
错误发生在以下流程:
用户点击”升级VIP”按钮
弹出VIP升级对话框
用户选择套餐并点击”立即支付”
在processPayment()方法中调用getAmount()时出错
问题根源在于:
selectedPlan变量只在支付成功后才被设置
但在支付验证过程中就调用了getAmount()
导致selectedPlan为null时尝试调用contains()方法
修复方案
以下是完整的修复代码,主要修改了VipUpgradeDialog类:
class VipUpgradeDialog extends JDialog {
private boolean upgraded = false;
private String selectedPlan;
private JComboBox<String> planComboBox;
private JTextField cardField;
private JTextField expiryField;
private JTextField cvvField;
public VipUpgradeDialog(JFrame parent) {
super(parent, "升级VIP会员", true);
setSize(450, 350);
setLocationRelativeTo(parent);
setLayout(new BorderLayout());
setResizable(false);
initUI();
}
private void initUI() {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
// 标题
JLabel titleLabel = new JLabel("升级VIP会员", SwingConstants.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
titleLabel.setBorder(new EmptyBorder(0, 0, 15, 0));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// VIP套餐选择
JPanel planPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
planPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
planPanel.add(new JLabel("选择套餐:"));
String[] plans = {
"月度VIP - ¥30", "季度VIP - ¥80", "年度VIP - ¥288"};
planComboBox = new JComboBox<>(plans);
planComboBox.setPreferredSize(new Dimension(180, 30));
planPanel.add(planComboBox);
mainPanel.add(planPanel, BorderLayout.NORTH);
// 支付信息表单
JPanel formPanel = new JPanel(new GridLayout(4, 2, 10, 10));
formPanel.setBorder(new EmptyBorder(10, 30, 10, 30));
formPanel.add(new JLabel("信用卡号:"));
cardField = new JTextField();
formPanel.add(cardField);
formPanel.add(new JLabel("有效期 (MM/YY):"));
expiryField = new JTextField();
formPanel.add(expiryField);
formPanel.add(new JLabel("CVV安全码:"));
cvvField = new JTextField();
formPanel.add(cvvField);
formPanel.add(new JLabel("持卡人姓名:"));
JTextField nameField = new JTextField();
formPanel.add(nameField);
mainPanel.add(formPanel, BorderLayout.CENTER);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 10));
JButton payButton = new JButton("立即支付");
payButton.setBackground(new Color(50, 205, 50));
payButton.setForeground(Color.WHITE);
payButton.setPreferredSize(new Dimension(120, 35));
payButton.addActionListener(e -> processPayment());
buttonPanel.add(payButton);
JButton cancelButton = new JButton("取消");
cancelButton.setBackground(new Color(220, 220, 220));
cancelButton.setPreferredSize(new Dimension(120, 35));
cancelButton.addActionListener(e -> setVisible(false));
buttonPanel.add(cancelButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void processPayment() {
// 首先获取当前选择的套餐
selectedPlan = (String) planComboBox.getSelectedItem();
String card = cardField.getText().trim();
String expiry = expiryField.getText().trim();
String cvv = cvvField.getText().trim();
// 简单验证
if (card.isEmpty() || expiry.isEmpty() || cvv.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写完整的支付信息", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!card.matches("\d{16}")) {
JOptionPane.showMessageDialog(this, "信用卡号必须是16位数字", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!expiry.matches("\d{2}/\d{2}")) {
JOptionPane.showMessageDialog(this, "有效期格式应为MM/YY", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!cvv.matches("\d{3}")) {
JOptionPane.showMessageDialog(this, "CVV安全码必须是3位数字", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 获取金额
double amount = getAmount();
// 模拟支付处理
JOptionPane.showMessageDialog(this,
"支付成功!
套餐: " + selectedPlan + "
金额: " + amount + "元",
"支付成功", JOptionPane.INFORMATION_MESSAGE);
upgraded = true;
setVisible(false);
}
public boolean isUpgraded() {
return upgraded;
}
public String getSelectedPlan() {
return selectedPlan;
}
public double getAmount() {
// 现在selectedPlan已经在processPayment()开始时设置
if (selectedPlan.contains("月度")) return 30.0;
if (selectedPlan.contains("季度")) return 80.0;
if (selectedPlan.contains("年度")) return 288.0;
return 0.0;
}
}
主要修改点
修改processPayment()方法:
在方法开始时就先获取并设置selectedPlan值
确保在调用getAmount()时selectedPlan已经有值
简化逻辑:
移除了不必要的selectedPlan赋值
确保支付验证通过后才显示成功消息
保持原有功能:
仍然支持三种VIP套餐
支付验证逻辑不变
对话框行为保持一致
其他建议
添加输入验证:
// 在processPayment()中添加更严格的验证
if (selectedPlan == null || selectedPlan.isEmpty()) {
JOptionPane.showMessageDialog(this, "请选择VIP套餐", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
改进错误处理:
public double getAmount() {
try {
if (selectedPlan.contains("月度")) return 30.0;
if (selectedPlan.contains("季度")) return 80.0;
if (selectedPlan.contains("年度")) return 288.0;
} catch (NullPointerException e) {
System.err.println("VIP套餐未选择");
}
return 0.0;
}
添加日志记录:
System.out.println("处理VIP升级: " + selectedPlan + ", 金额: " + getAmount());
这个修复方案确保了在调用getAmount()方法时selectedPlan已经被正确初始化,从而避免了NullPointerException。同时保持了原有的所有功能不变。
Java聊天室项目文档
1. 项目概述
1.1 项目背景
本项目是一个基于Java的图形化界面聊天室系统,结合了实时通信、VIP会员体系和支付功能。系统采用C/S架构,包含客户端GUI应用和Socket服务器端,后端使用MySQL数据库存储用户数据和聊天记录。
1.2 系统架构
客户端应用 (GUI) ↔ Socket通信 ↔ 服务器 ↔ MySQL数据库
1.3 技术栈
客户端:Java Swing GUI,Socket通信
服务器端:Java Socket服务器,多线程处理
数据库:MySQL 8.0
支付系统:模拟支付网关(可扩展为真实支付接口)
2. 功能特性
2.1 核心功能
| 功能模块 | 功能描述 | 用户类型 |
|---|---|---|
| 用户认证 | 注册新账号、登录系统 | 所有用户 |
| 实时聊天 | 发送/接收文本消息、查看历史记录 | 所有用户 |
| 消息样式 | 自定义字体和颜色 | 所有用户 |
| VIP升级 | 选择套餐、模拟支付升级 | 普通用户 |
| VIP特权 | 发送图片/文件、消息加密、导出记录 | VIP用户 |
| 系统通知 | 用户加入/离开通知、系统消息 | 所有用户 |
2.2 VIP特权详情
图片发送:支持上传和发送JPG/PNG图片
文件传输:支持发送任意类型文件
消息加密:使用简单加密算法保护消息隐私
记录导出:将聊天记录导出为HTML文件
专属标识:VIP用户在聊天中显示特殊标识
2.3 支付套餐
| 套餐类型 | 价格 | 有效期 |
|---|---|---|
| 月度VIP | ¥30 | 30天 |
| 季度VIP | ¥80 | 90天 |
| 年度VIP | ¥288 | 365天 |
3. 技术架构
3.1 系统组件
3.2 数据库设计
表:users
| 字段 | 类型 | 描述 |
|---|---|---|
| id | INT | 主键 |
| username | VARCHAR(50) | 用户名 |
| password | VARCHAR(255) | 密码(SHA256加密) |
| is_vip | BOOLEAN | VIP状态 |
| created_at | TIMESTAMP | 创建时间 |
表:vip_payments
| 字段 | 类型 | 描述 |
|---|---|---|
| id | INT | 主键 |
| user_id | INT | 用户ID |
| amount | DECIMAL(10,2) | 支付金额 |
| payment_date | TIMESTAMP | 支付时间 |
| plan | VARCHAR(50) | 套餐类型 |
4. 部署指南
4.1 环境要求
Java 11+
MySQL 8.0+
网络环境(客户端与服务器可通信)
4.2 数据库部署
CREATE DATABASE chat_app;
USE chat_app;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
is_vip BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE vip_payments (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
payment_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
plan VARCHAR(50) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
4.3 服务器部署
编译服务器代码:
javac -cp .:lib/mysql-connector-java-x.x.xx.jar server/*.java
运行服务器:
java -cp .:lib/mysql-connector-java-x.x.xx.jar server.ChatServer
4.4 客户端部署
编译客户端代码:
javac client/ChatApplication.java
运行客户端:
java client.ChatApplication
5. 使用说明
5.1 登录界面
输入服务器地址和端口
新用户点击”注册”按钮创建账号
老用户输入用户名密码登录
5.2 主界面
聊天区域:显示实时聊天消息
功能按钮:
升级VIP:打开支付对话框
发送图片:VIP用户可用
发送文件:VIP用户可用
加密消息:VIP用户可用
导出记录:VIP用户可用
样式选择:选择字体和颜色
输入区域:输入消息内容
5.3 VIP升级流程
点击”升级VIP”按钮
选择套餐(月度/季度/年度)
填写支付信息(模拟)
完成支付后自动启用VIP功能
6. 赚钱运营方案
6.1 盈利模式
VIP会员订阅
提供月度/季度/年度三种套餐
价格策略:年度套餐提供最大折扣(约8折)
自动续费选项:增加用户留存率
虚拟商品销售
聊天表情包:¥5-¥15/套
特殊昵称特效:¥10/月
聊天主题皮肤:¥8-¥20/套
广告展示
非VIP用户聊天界面底部展示广告
VIP用户可付费移除广告
企业定制服务
私有化部署:为企业提供专属聊天室
API接口服务:按调用量收费
6.2 用户增长策略
免费增值模式
基础功能免费吸引用户
VIP功能提供高级体验
新用户赠送7天VIP试用
邀请奖励机制
邀请新用户注册双方获VIP天数
建立多级分销体系
内容生态建设
创建主题聊天室(游戏、学习、兴趣)
引入KOL主持专属聊天室
6.3 支付系统扩展
集成真实支付
支付宝/微信支付接入
银行卡支付支持
第三方支付平台(Stripe、PayPal)
支付安全措施
PCI DSS合规
敏感数据加密存储
交易监控系统
6.4 成本控制
| 成本项目 | 控制策略 |
|---|---|
| 服务器成本 | 使用云服务弹性扩容 |
| 开发维护 | 开源核心模块,社区贡献 |
| 支付手续费 | 与支付平台谈判优惠费率 |
| 营销推广 | 社交媒体+KOL合作 |
6.5 收入预测
| 用户规模 | 转化率 | 月收入预测 |
|---|---|---|
| 1,000用户 | 5% | ¥1,500 |
| 10,000用户 | 8% | ¥24,000 |
| 100,000用户 | 10% | ¥300,000 |
7. 发展路线图
短期目标 (1-3个月)
完成真实支付接口集成
实现移动端适配
建立基础用户社区
中期目标 (4-6个月)
推出虚拟商品商店
开发Android/iOS客户端
实现聊天室频道管理
长期目标 (7-12个月)
推出企业版解决方案
建立开发者平台
国际化版本开发
8. 风险与对策
| 风险类型 | 应对策略 |
|---|---|
| 支付安全风险 | 定期安全审计,购买网络安全保险 |
| 用户隐私风险 | 严格遵循GDPR,数据最小化原则 |
| 市场竞争风险 | 差异化VIP功能,持续创新 |
| 技术债务风险 | 建立代码审查制度,定期重构 |
通过实施以上运营方案,本项目预计可在6-12个月内实现盈亏平衡,并在18-24个月获得稳定盈利。VIP会员订阅作为核心收入来源,配合虚拟商品销售和广告业务,可构建多元化的收入结构。



















暂无评论内容