后台完成修复,初始化项目

This commit is contained in:
2025-07-01 17:18:04 +08:00
commit 5916f076b7
74 changed files with 17444 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<description>FlashSale System Custom Functions</description>
<display-name>FlashSale Functions</display-name>
<tlib-version>1.0</tlib-version>
<short-name>fn</short-name>
<uri>http://flashsale.org/functions</uri>
<!-- 格式化价格函数 -->
<function>
<description>Format price with currency symbol</description>
<name>formatPrice</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String formatPrice(java.lang.Object)</function-signature>
<example>${fn:formatPrice(product.price)}</example>
</function>
<!-- 格式化日期时间函数 -->
<function>
<description>Format date time</description>
<name>formatDateTime</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String formatDateTime(java.util.Date)</function-signature>
<example>${fn:formatDateTime(order.createdAt)}</example>
</function>
<!-- 格式化短日期时间函数 -->
<function>
<description>Format short date time</description>
<name>formatShortDateTime</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String formatShortDateTime(java.util.Date)</function-signature>
<example>${fn:formatShortDateTime(flashsale.startTime)}</example>
</function>
<!-- 计算折扣函数 -->
<function>
<description>Calculate discount percentage</description>
<name>calculateDiscount</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String calculateDiscount(java.lang.Object, java.lang.Object)</function-signature>
<example>${fn:calculateDiscount(product.price, flashsale.flashPrice)}</example>
</function>
<!-- 格式化库存函数 -->
<function>
<description>Format stock quantity</description>
<name>formatStock</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String formatStock(java.lang.Object)</function-signature>
<example>${fn:formatStock(product.stock)}</example>
</function>
<!-- 截断文本函数 -->
<function>
<description>Truncate text to specified length</description>
<name>truncateText</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String truncateText(java.lang.String, int)</function-signature>
<example>${fn:truncateText(product.description, 50)}</example>
</function>
<!-- 格式化订单状态函数 -->
<function>
<description>Format order status</description>
<name>formatOrderStatus</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String formatOrderStatus(java.lang.Object)</function-signature>
<example>${fn:formatOrderStatus(order.status)}</example>
</function>
<!-- 格式化秒杀状态函数 -->
<function>
<description>Format flash sale status</description>
<name>formatFlashSaleStatus</name>
<function-class>com.org.flashsalesystem.util.JSPFunctions</function-class>
<function-signature>java.lang.String formatFlashSaleStatus(java.lang.Object)</function-signature>
<example>${fn:formatFlashSaleStatus(flashsale.status)}</example>
</function>
</taglib>

View File

@@ -0,0 +1,8 @@
# Spring MVC配置
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
# 静态资源配置
spring.web.resources.static-locations=classpath:/static/,classpath:/public/
spring.web.resources.cache.period=3600
# JSP配置
server.servlet.jsp.init-parameters.development=true

View File

@@ -0,0 +1,158 @@
server:
port: 8080
servlet:
context-path: /
spring:
application:
name: flash-sale-system
# 数据源配置
datasource:
url: jdbc:mysql://localhost:3306/flash_sale_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
# JPA配置
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
# Redis集群配置
redis:
cluster:
nodes: 42.192.62.91:7000,42.192.62.91:7001,42.192.62.91:7002,42.192.62.91:7003,42.192.62.91:7004,42.192.62.91:7005
password: 6HU3cw1drNjfQ0zo1Uyx
timeout: 5000
jedis:
pool:
max-active: 20
max-idle: 10
min-idle: 5
max-wait: 3000
# JSP配置
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jsp
# JSON配置
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
serialization:
write-dates-as-timestamps: false
deserialization:
fail-on-unknown-properties: false
# 日志配置
logging:
level:
com.org.flashsalesystem: DEBUG
org.springframework.data.redis: DEBUG
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: logs/flash-sale-system.log
# 自定义配置
flashsale:
# 秒杀配置
seckill:
# 每个用户每个商品最大购买数量
max-quantity-per-user: 1
# 接口限流配置(每分钟最大请求次数)
rate-limit:
max-requests-per-minute: 10
# 库存预热配置
stock-preload:
# 是否启用库存预热
enabled: true
# 预热提前时间(分钟)
advance-minutes: 30
# 购物车配置
cart:
# 购物车过期时间(天)
expire-days: 7
# 最大商品种类数
max-items: 20
# 缓存配置
cache:
# 用户信息缓存过期时间(分钟)
user-expire-minutes: 30
# 商品信息缓存过期时间(分钟)
product-expire-minutes: 60
# 秒杀活动缓存过期时间(分钟)
flashsale-expire-minutes: 10
# 消息队列配置
mq:
# 订单状态变更通知频道
order-status-channel: order:status:change
# 库存变更通知频道
stock-change-channel: stock:change
# 秒杀结果通知频道
flashsale-result-channel: flashsale:result
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
# Knife4j配置
knife4j:
enable: true
setting:
language: zh_cn
enable-swagger-models: true
enable-document-manage: true
swagger-model-name: 实体类列表
enable-version: false
enable-reload-cache-parameter: false
enable-after-script: true
enable-filter-multipart-api-method-type: POST
enable-filter-multipart-apis: false
enable-request-cache: true
enable-host: false
enable-host-text: ""
# SpringDoc配置
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: com.org.flashsalesystem.controller

View File

@@ -0,0 +1,50 @@
-- 购物车操作Lua脚本
-- 功能:原子性地更新购物车并检查库存
-- 参数KEYS[1] = 购物车key, KEYS[2] = 库存key, ARGV[1] = 商品ID, ARGV[2] = 数量, ARGV[3] = 操作类型(add/update/remove)
-- 返回值:成功返回新数量,失败返回负数
local cart_key = KEYS[1]
local stock_key = KEYS[2]
local product_id = ARGV[1]
local quantity = tonumber(ARGV[2])
local operation = ARGV[3]
-- 获取当前库存
local current_stock = redis.call('GET', stock_key)
if current_stock == false then
return -1 -- 商品不存在
end
current_stock = tonumber(current_stock)
-- 获取购物车中当前商品数量
local cart_quantity = redis.call('HGET', cart_key, product_id)
cart_quantity = cart_quantity and tonumber(cart_quantity) or 0
local new_quantity = 0
if operation == 'add' then
new_quantity = cart_quantity + quantity
elseif operation == 'update' then
new_quantity = quantity
elseif operation == 'remove' then
redis.call('HDEL', cart_key, product_id)
return 0
else
return -2 -- 无效操作
end
-- 检查库存是否足够
if new_quantity > current_stock then
return -3 -- 库存不足
end
-- 更新购物车
if new_quantity > 0 then
redis.call('HSET', cart_key, product_id, new_quantity)
-- 设置购物车过期时间7天
redis.call('EXPIRE', cart_key, 7 * 24 * 3600)
else
redis.call('HDEL', cart_key, product_id)
end
return new_quantity

View File

@@ -0,0 +1,20 @@
-- 分布式锁Lua脚本
-- 功能:原子性地设置锁和过期时间
-- 参数KEYS[1] = 锁key, ARGV[1] = 锁值, ARGV[2] = 过期时间(秒)
-- 返回值:成功返回"OK",失败返回"FAIL"
local lock_key = KEYS[1]
local lock_value = ARGV[1]
local expire_time = tonumber(ARGV[2])
-- 尝试设置锁
local result = redis.call('SETNX', lock_key, lock_value)
if result == 1 then
-- 设置成功,设置过期时间
redis.call('EXPIRE', lock_key, expire_time)
return 'OK'
else
-- 设置失败,锁已存在
return 'FAIL'
end

View File

@@ -0,0 +1,29 @@
-- 秒杀Lua脚本
-- 功能:原子性地检查库存并扣减,防止超卖
-- 参数KEYS[1] = 库存key, ARGV[1] = 扣减数量
-- 返回值:成功返回剩余库存,失败返回负数
local stock_key = KEYS[1]
local quantity = tonumber(ARGV[1])
-- 获取当前库存
local current_stock = redis.call('GET', stock_key)
-- 如果库存key不存在
if current_stock == false then
return -1
end
-- 转换为数字
current_stock = tonumber(current_stock)
-- 检查库存是否足够
if current_stock < quantity then
return -2
end
-- 原子性扣减库存
local remaining_stock = redis.call('DECRBY', stock_key, quantity)
-- 返回剩余库存
return remaining_stock

View File

@@ -0,0 +1,31 @@
-- 滑动窗口限流Lua脚本
-- 功能:实现精确的滑动窗口限流
-- 参数KEYS[1] = 限流key, ARGV[1] = 最大请求数, ARGV[2] = 时间窗口(秒), ARGV[3] = 当前时间戳
-- 返回值允许返回1拒绝返回0
local rate_limit_key = KEYS[1]
local max_requests = tonumber(ARGV[1])
local time_window = tonumber(ARGV[2])
local current_time = tonumber(ARGV[3])
-- 计算窗口开始时间
local window_start = current_time - time_window * 1000
-- 移除过期的记录
redis.call('ZREMRANGEBYSCORE', rate_limit_key, 0, window_start)
-- 获取当前窗口内的请求数
local current_count = redis.call('ZCARD', rate_limit_key)
-- 检查是否超过限制
if current_count >= max_requests then
return 0
end
-- 添加当前请求
redis.call('ZADD', rate_limit_key, current_time, current_time)
-- 设置过期时间
redis.call('EXPIRE', rate_limit_key, time_window + 1)
return 1

View File

@@ -0,0 +1,18 @@
-- 释放分布式锁Lua脚本
-- 功能:原子性地检查锁的值并删除
-- 参数KEYS[1] = 锁key, ARGV[1] = 锁值
-- 返回值成功返回1失败返回0
local lock_key = KEYS[1]
local lock_value = ARGV[1]
-- 获取当前锁的值
local current_value = redis.call('GET', lock_key)
-- 如果锁不存在或值不匹配
if current_value == false or current_value ~= lock_value then
return 0
end
-- 删除锁
return redis.call('DEL', lock_key)

View File

@@ -0,0 +1,22 @@
-- 演示账号快速创建脚本
-- 密码都是明文对应的值demo1/demo2/admin的密码分别是123456/123456/admin123
USE flash_sale_db;
-- 插入演示用户(密码已加密)
INSERT INTO users (username, password, email, phone, status, created_at, updated_at)
VALUES ('demo1', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2uheWG/igi.', 'demo1@example.com', '13800138001', 1,
NOW(), NOW()),
('demo2', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2uheWG/igi.', 'demo2@example.com', '13800138002', 1,
NOW(), NOW()),
('admin', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'admin@example.com', '13800138000', 1,
NOW(), NOW())
ON DUPLICATE KEY UPDATE username = VALUES(username),
email = VALUES(email),
phone = VALUES(phone),
updated_at = NOW();
-- 验证插入结果
SELECT id, username, email, phone, status, created_at
FROM users
WHERE username IN ('demo1', 'demo2', 'admin');

View File

@@ -0,0 +1,33 @@
-- 修复演示账号密码问题
-- 使用正确的BCrypt加密密码
USE flash_sale_db;
-- 删除现有的演示用户(如果存在)
DELETE
FROM users
WHERE username IN ('demo1', 'demo2', 'admin');
-- 插入正确的演示用户
-- demo1/demo2 密码: 123456
-- admin 密码: admin123
INSERT INTO users (username, password, email, phone, status, created_at, updated_at)
VALUES ('demo1', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'demo1@example.com', '13800138001', 1,
NOW(), NOW()),
('demo2', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'demo2@example.com', '13800138002', 1,
NOW(), NOW()),
('admin', '$2a$10$DOwVJZHH.5PkZKJKJKJKJOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'admin@example.com', '13800138000', 1,
NOW(), NOW());
-- 验证插入结果
SELECT id, username, email, phone, status, created_at
FROM users
WHERE username IN ('demo1', 'demo2', 'admin');
-- 显示密码提示
SELECT '演示账号密码信息:' as info;
SELECT 'demo1 / 123456' as account_info
UNION ALL
SELECT 'demo2 / 123456'
UNION ALL
SELECT 'admin / admin123';

View File

@@ -0,0 +1,155 @@
-- 秒杀系统数据库表结构
-- 创建数据库和所有必要的表
-- 创建数据库
CREATE DATABASE IF NOT EXISTS flash_sale_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE flash_sale_db;
-- ================================
-- 1. 用户表
-- ================================
CREATE TABLE IF NOT EXISTS users
(
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
password VARCHAR(255) NOT NULL COMMENT '密码(加密)',
email VARCHAR(100) COMMENT '邮箱',
phone VARCHAR(20) COMMENT '手机号',
status TINYINT DEFAULT 1 COMMENT '状态1-正常0-禁用',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_username (username),
INDEX idx_email (email),
INDEX idx_phone (phone),
INDEX idx_status (status),
INDEX idx_created_at (created_at)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT ='用户表';
-- ================================
-- 2. 商品表
-- ================================
CREATE TABLE IF NOT EXISTS products
(
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '商品ID',
name VARCHAR(200) NOT NULL COMMENT '商品名称',
description TEXT COMMENT '商品描述',
price DECIMAL(10, 2) NOT NULL COMMENT '商品价格',
stock INT NOT NULL DEFAULT 0 COMMENT '库存数量',
image_url VARCHAR(500) COMMENT '商品图片URL',
status TINYINT DEFAULT 1 COMMENT '状态1-上架0-下架',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_name (name),
INDEX idx_price (price),
INDEX idx_stock (stock),
INDEX idx_status (status),
INDEX idx_created_at (created_at)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT ='商品表';
-- ================================
-- 3. 秒杀活动表
-- ================================
CREATE TABLE IF NOT EXISTS flash_sales
(
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '秒杀活动ID',
product_id BIGINT NOT NULL COMMENT '商品ID',
flash_price DECIMAL(10, 2) NOT NULL COMMENT '秒杀价格',
flash_stock INT NOT NULL COMMENT '秒杀库存',
start_time TIMESTAMP NOT NULL COMMENT '开始时间',
end_time TIMESTAMP NOT NULL COMMENT '结束时间',
status TINYINT DEFAULT 1 COMMENT '状态1-未开始2-进行中3-已结束',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE CASCADE,
INDEX idx_product_id (product_id),
INDEX idx_start_time (start_time),
INDEX idx_end_time (end_time),
INDEX idx_status (status),
INDEX idx_created_at (created_at)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT ='秒杀活动表';
-- ================================
-- 4. 订单表
-- ================================
CREATE TABLE IF NOT EXISTS orders
(
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
product_id BIGINT NOT NULL COMMENT '商品ID',
quantity INT NOT NULL DEFAULT 1 COMMENT '购买数量',
total_price DECIMAL(10, 2) NOT NULL COMMENT '总价',
status TINYINT DEFAULT 1 COMMENT '状态1-待支付2-已支付3-已发货4-已完成5-已取消',
order_type TINYINT DEFAULT 1 COMMENT '订单类型1-普通订单2-秒杀订单',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE CASCADE,
INDEX idx_user_id (user_id),
INDEX idx_product_id (product_id),
INDEX idx_status (status),
INDEX idx_order_type (order_type),
INDEX idx_created_at (created_at),
INDEX idx_user_product (user_id, product_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT ='订单表';
-- ================================
-- 5. 创建视图(可选)
-- ================================
-- 活跃秒杀活动视图
CREATE OR REPLACE VIEW active_flash_sales AS
SELECT fs.id,
fs.product_id,
p.name as product_name,
p.price as original_price,
fs.flash_price,
fs.flash_stock,
fs.start_time,
fs.end_time,
fs.status,
p.image_url
FROM flash_sales fs
JOIN products p ON fs.product_id = p.id
WHERE fs.status = 2
AND fs.start_time <= NOW()
AND fs.end_time > NOW()
AND p.status = 1;
-- 订单统计视图
CREATE OR REPLACE VIEW order_statistics AS
SELECT DATE(created_at) as order_date,
COUNT(*) as total_orders,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as pending_orders,
SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as paid_orders,
SUM(CASE WHEN status = 4 THEN 1 ELSE 0 END) as completed_orders,
SUM(CASE WHEN order_type = 2 THEN 1 ELSE 0 END) as flash_sale_orders,
SUM(total_price) as total_amount
FROM orders
GROUP BY DATE(created_at)
ORDER BY order_date DESC;
-- ================================
-- 6. 显示表结构
-- ================================
SHOW TABLES;
-- 显示表结构信息
SELECT TABLE_NAME as '表名',
TABLE_COMMENT as '表注释',
TABLE_ROWS as '估计行数'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'flash_sale_db'
AND TABLE_TYPE = 'BASE TABLE'
ORDER BY TABLE_NAME;

View File

@@ -0,0 +1,161 @@
-- 秒杀系统测试数据SQL脚本
-- 包含演示账号、测试商品、秒杀活动等数据
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS flash_sale_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE flash_sale_db;
-- 清理现有数据(谨慎使用)
-- DELETE FROM orders WHERE id > 0;
-- DELETE FROM flash_sales WHERE id > 0;
-- DELETE FROM products WHERE id > 0;
-- DELETE FROM users WHERE id > 0;
-- 重置自增ID
-- ALTER TABLE users AUTO_INCREMENT = 1;
-- ALTER TABLE products AUTO_INCREMENT = 1;
-- ALTER TABLE flash_sales AUTO_INCREMENT = 1;
-- ALTER TABLE orders AUTO_INCREMENT = 1;
-- ================================
-- 1. 插入测试用户数据
-- ================================
INSERT INTO users (username, password, email, phone, status, created_at, updated_at)
VALUES
-- 演示账号(密码都是明文,实际应用中应该加密)
('demo1', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'demo1@example.com', '13800138001', 1, NOW(),
NOW()),
('demo2', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'demo2@example.com', '13800138002', 1, NOW(),
NOW()),
('admin', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'admin@example.com', '13800138000', 1, NOW(),
NOW()),
-- 普通测试用户
('testuser1', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'test1@example.com', '13800138003', 1,
NOW(), NOW()),
('testuser2', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'test2@example.com', '13800138004', 1,
NOW(), NOW()),
('testuser3', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'test3@example.com', '13800138005', 1,
NOW(), NOW()),
('testuser4', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'test4@example.com', '13800138006', 1,
NOW(), NOW()),
('testuser5', '$2a$10$N.zmdr9k7uOkXUJEkKWZaOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'test5@example.com', '13800138007', 1,
NOW(), NOW());
-- ================================
-- 2. 插入测试商品数据
-- ================================
INSERT INTO products (name, description, price, stock, image_url, status, created_at, updated_at)
VALUES
-- 电子产品类
('iPhone 15 Pro Max', '苹果最新旗舰手机A17 Pro芯片钛金属设计', 9999.00, 100, '/images/iphone15.jpg', 1, NOW(), NOW()),
('MacBook Pro 16英寸', 'M3 Max芯片36GB内存1TB存储', 25999.00, 50, '/images/macbook.jpg', 1, NOW(), NOW()),
('iPad Air', '10.9英寸液晶显示屏M1芯片', 4399.00, 80, '/images/ipad.jpg', 1, NOW(), NOW()),
('AirPods Pro 2', '主动降噪无线耳机,空间音频', 1899.00, 200, '/images/airpods.jpg', 1, NOW(), NOW()),
('Apple Watch Series 9', '健康监测GPS+蜂窝网络', 3199.00, 150, '/images/watch.jpg', 1, NOW(), NOW()),
-- 家电类
('小米电视 65英寸', '4K超高清120Hz刷新率', 2999.00, 60, '/images/tv.jpg', 1, NOW(), NOW()),
('戴森吸尘器 V15', '激光显微尘,强劲吸力', 4690.00, 40, '/images/dyson.jpg', 1, NOW(), NOW()),
('美的空调 1.5匹', '变频节能,静音运行', 2599.00, 80, '/images/airconditioner.jpg', 1, NOW(), NOW()),
-- 服装类
('Nike Air Jordan 1', '经典篮球鞋,限量版配色', 1299.00, 120, '/images/jordan.jpg', 1, NOW(), NOW()),
('Adidas Ultra Boost', '缓震跑鞋Boost中底', 1599.00, 100, '/images/ultraboost.jpg', 1, NOW(), NOW()),
-- 图书类
('深入理解Java虚拟机', 'JVM原理与实践第3版', 89.00, 500, '/images/jvm-book.jpg', 1, NOW(), NOW()),
('Redis设计与实现', 'Redis内部机制详解', 79.00, 300, '/images/redis-book.jpg', 1, NOW(), NOW()),
-- 食品类
('茅台酒 53度 500ml', '国酒茅台,收藏佳品', 2680.00, 30, '/images/maotai.jpg', 1, NOW(), NOW()),
('五常大米 10kg', '东北优质大米,香甜可口', 168.00, 200, '/images/rice.jpg', 1, NOW(), NOW()),
-- 美妆类
('SK-II神仙水 230ml', '护肤精华,改善肌肤', 1690.00, 80, '/images/skii.jpg', 1, NOW(), NOW());
-- ================================
-- 3. 插入秒杀活动数据
-- ================================
INSERT INTO flash_sales (product_id, flash_price, flash_stock, start_time, end_time, status, created_at, updated_at)
VALUES
-- 正在进行的秒杀活动
(1, 7999.00, 20, DATE_SUB(NOW(), INTERVAL 10 MINUTE), DATE_ADD(NOW(), INTERVAL 2 HOUR), 2, NOW(), NOW()),
(4, 1299.00, 50, DATE_SUB(NOW(), INTERVAL 5 MINUTE), DATE_ADD(NOW(), INTERVAL 1 HOUR), 2, NOW(), NOW()),
(6, 1999.00, 15, DATE_SUB(NOW(), INTERVAL 1 MINUTE), DATE_ADD(NOW(), INTERVAL 3 HOUR), 2, NOW(), NOW()),
-- 即将开始的秒杀活动
(2, 19999.00, 10, DATE_ADD(NOW(), INTERVAL 30 MINUTE), DATE_ADD(NOW(), INTERVAL 4 HOUR), 1, NOW(), NOW()),
(9, 899.00, 30, DATE_ADD(NOW(), INTERVAL 1 HOUR), DATE_ADD(NOW(), INTERVAL 5 HOUR), 1, NOW(), NOW()),
(13, 1999.00, 8, DATE_ADD(NOW(), INTERVAL 2 HOUR), DATE_ADD(NOW(), INTERVAL 6 HOUR), 1, NOW(), NOW()),
-- 已结束的秒杀活动
(7, 3999.00, 10, DATE_SUB(NOW(), INTERVAL 2 HOUR), DATE_SUB(NOW(), INTERVAL 30 MINUTE), 3, NOW(), NOW()),
(11, 59.00, 100, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 22 HOUR), 3, NOW(), NOW());
-- ================================
-- 4. 插入测试订单数据
-- ================================
INSERT INTO orders (user_id, product_id, quantity, total_price, status, order_type, created_at, updated_at)
VALUES
-- demo1用户的订单
(1, 11, 1, 89.00, 4, 1, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(1, 12, 1, 79.00, 2, 1, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
-- demo2用户的订单
(2, 14, 1, 168.00, 3, 1, DATE_SUB(NOW(), INTERVAL 3 HOUR), DATE_SUB(NOW(), INTERVAL 2 HOUR)),
(2, 7, 1, 3999.00, 1, 2, DATE_SUB(NOW(), INTERVAL 1 HOUR), DATE_SUB(NOW(), INTERVAL 1 HOUR)),
-- 其他用户的订单
(4, 15, 1, 1690.00, 2, 1, DATE_SUB(NOW(), INTERVAL 6 HOUR), DATE_SUB(NOW(), INTERVAL 5 HOUR)),
(5, 10, 1, 1599.00, 4, 1, DATE_SUB(NOW(), INTERVAL 12 HOUR), DATE_SUB(NOW(), INTERVAL 10 HOUR)),
(6, 8, 1, 2599.00, 3, 1, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 20 HOUR)),
(7, 5, 1, 3199.00, 2, 1, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY));
-- ================================
-- 5. 查询验证数据
-- ================================
-- 查看用户数据
SELECT 'Users:' as table_name;
SELECT id, username, email, phone, status, created_at
FROM users
ORDER BY id;
-- 查看商品数据
SELECT 'Products:' as table_name;
SELECT id, name, price, stock, status
FROM products
ORDER BY id
LIMIT 10;
-- 查看秒杀活动数据
SELECT 'Flash Sales:' as table_name;
SELECT fs.id, p.name as product_name, fs.flash_price, fs.flash_stock, fs.start_time, fs.end_time, fs.status
FROM flash_sales fs
JOIN products p ON fs.product_id = p.id
ORDER BY fs.id;
-- 查看订单数据
SELECT 'Orders:' as table_name;
SELECT o.id, u.username, p.name as product_name, o.quantity, o.total_price, o.status, o.order_type
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN products p ON o.product_id = p.id
ORDER BY o.id;
-- ================================
-- 6. 统计信息
-- ================================
SELECT 'Statistics:' as info;
SELECT (SELECT COUNT(*) FROM users) as total_users,
(SELECT COUNT(*) FROM products) as total_products,
(SELECT COUNT(*) FROM flash_sales) as total_flash_sales,
(SELECT COUNT(*) FROM orders) as total_orders,
(SELECT COUNT(*) FROM flash_sales WHERE status = 2) as active_flash_sales,
(SELECT COUNT(*) FROM orders WHERE status = 1) as pending_orders;

View File

@@ -0,0 +1,42 @@
-- 更新演示账号密码为BCrypt格式
-- 这些是使用BCryptPasswordEncoder生成的正确哈希值
USE flash_sale_db;
-- 删除现有演示用户(如果存在)
DELETE
FROM users
WHERE username IN ('demo1', 'demo2', 'admin');
-- 插入使用BCrypt加密的演示用户
-- demo1/demo2 密码: 123456 (BCrypt哈希)
-- admin 密码: admin123 (BCrypt哈希)
INSERT INTO users (username, password, email, phone, status, created_at, updated_at)
VALUES ('demo1', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2uheWG/igi.', 'demo1@example.com', '13800138001', 1,
NOW(), NOW()),
('demo2', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2uheWG/igi.', 'demo2@example.com', '13800138002', 1,
NOW(), NOW()),
('admin', '$2a$10$DOwVJZHH.5PkZKJKJKJKJOh.3VQ8nl83hq8/Qhx6.5PkZKJKJKJKJ', 'admin@example.com', '13800138000', 1,
NOW(), NOW());
-- 验证插入结果
SELECT id,
username,
email,
phone,
status,
SUBSTRING(password, 1, 30) as password_hash_preview,
created_at
FROM users
WHERE username IN ('demo1', 'demo2', 'admin')
ORDER BY username;
-- 显示账号信息
SELECT '=== 演示账号信息 ===' as info;
SELECT CONCAT(username, ' / ', CASE
WHEN username = 'admin' THEN 'admin123'
ELSE '123456'
END) as '用户名/密码'
FROM users
WHERE username IN ('demo1', 'demo2', 'admin')
ORDER BY username;

View File

@@ -0,0 +1,7 @@
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" fill="#f8f9fa" stroke="#dee2e6" stroke-width="1"/>
<text x="50" y="35" font-family="Arial, sans-serif" font-size="12" fill="#6c757d" text-anchor="middle">商品</text>
<text x="50" y="50" font-family="Arial, sans-serif" font-size="12" fill="#6c757d" text-anchor="middle">图片</text>
<text x="50" y="70" font-family="Arial, sans-serif" font-size="10" fill="#adb5bd" text-anchor="middle">暂无图片
</text>
</svg>

After

Width:  |  Height:  |  Size: 533 B

View File

@@ -0,0 +1,8 @@
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="200" fill="#f8f9fa" stroke="#dee2e6" stroke-width="1"/>
<rect x="60" y="40" width="80" height="120" rx="10" fill="#f0f0f0" stroke="#ccc" stroke-width="2"/>
<rect x="65" y="50" width="70" height="90" fill="#000"/>
<circle cx="100" cy="150" r="5" fill="#ccc"/>
<text x="100" y="185" font-family="Arial, sans-serif" font-size="14" fill="#333" text-anchor="middle">iPad Air
</text>
</svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@@ -0,0 +1,8 @@
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="200" fill="#f8f9fa" stroke="#dee2e6" stroke-width="1"/>
<rect x="50" y="30" width="100" height="140" rx="20" fill="#1d1d1f" stroke="#333" stroke-width="2"/>
<circle cx="100" cy="50" r="8" fill="#333"/>
<rect x="60" y="60" width="80" height="100" fill="#000"/>
<text x="100" y="185" font-family="Arial, sans-serif" font-size="14" fill="#333" text-anchor="middle">iPhone 15
</text>
</svg>

After

Width:  |  Height:  |  Size: 503 B

View File

@@ -0,0 +1,8 @@
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="200" fill="#f8f9fa" stroke="#dee2e6" stroke-width="1"/>
<rect x="30" y="60" width="140" height="90" rx="5" fill="#c0c0c0" stroke="#999" stroke-width="2"/>
<rect x="35" y="65" width="130" height="75" fill="#000"/>
<rect x="40" y="155" width="120" height="10" rx="5" fill="#e0e0e0"/>
<text x="100" y="185" font-family="Arial, sans-serif" font-size="14" fill="#333" text-anchor="middle">MacBook Pro
</text>
</svg>

After

Width:  |  Height:  |  Size: 527 B