7.8 本地+线上两套配置

This commit is contained in:
zhangsan 2025-07-09 16:06:35 +08:00
parent ee61a75512
commit 43d4994d30
16 changed files with 1114 additions and 85 deletions

View File

@ -11,57 +11,14 @@ services:
container_name: group-buy-market-front
restart: always
ports:
- '80:80'
- '443:443'
- '86:80'
volumes:
- ./nginx/html:/usr/share/nginx/html
privileged: true
networks:
- my-network
# 2. MySQL
mysql:
image: mysql:8.0
container_name: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
ports:
- '13306:3306'
volumes:
- ./mysql/my.cnf:/etc/mysql/conf.d/mysql.cnf:ro
- ./mysql/sql:/docker-entrypoint-initdb.d
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
interval: 5s
timeout: 10s
retries: 10
start_period: 15s
networks:
- my-network
# 3. Redis
redis:
image: redis:6.2
container_name: redis
restart: always
hostname: redis
ports:
- '16379:6379'
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
command: redis-server /usr/local/etc/redis/redis.conf
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 3
networks:
- my-network
# 4. Java 后端
# 2. Java 后端
group-buying-sys:
build:
context: ../.. # 从 docs/dev-ops 回到项目根
@ -96,39 +53,3 @@ services:
networks:
- my-network
# 5. phpMyAdmin只有在 webui profile 下才启动)
phpmyadmin:
image: phpmyadmin:5.2.1
container_name: phpmyadmin
hostname: phpmyadmin
depends_on:
mysql:
condition: service_healthy
ports:
- '8899:80'
environment:
- PMA_HOST=mysql
- PMA_PORT=3306
- MYSQL_ROOT_PASSWORD=123456
networks:
- my-network
profiles: ["webui"]
# 6. Redis Commander只有在 webui profile 下才启动)
redis-admin:
image: spryker/redis-commander:0.8.0
container_name: redis-admin
hostname: redis-commander
restart: always
depends_on:
redis:
condition: service_healthy
ports:
- '8081:8081'
environment:
- REDIS_HOSTS=local:redis:6379
- HTTP_USER=admin
- HTTP_PASSWORD=admin
networks:
- my-network
profiles: ["webui"]

View File

@ -0,0 +1,133 @@
version: '3.8'
networks:
my-network:
driver: bridge
services:
# 1. 前端
group-buy-market-front:
image: nginx:alpine
container_name: group-buy-market-front
restart: always
ports:
- '86:80'
volumes:
- ./nginx/html:/usr/share/nginx/html
privileged: true
networks:
- my-network
# 2. MySQL
mysql:
image: mysql:8.0
container_name: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
ports:
- '13306:3306'
volumes:
- ./mysql/my.cnf:/etc/mysql/conf.d/mysql.cnf:ro
- ./mysql/sql:/docker-entrypoint-initdb.d
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
interval: 5s
timeout: 10s
retries: 10
start_period: 15s
networks:
- my-network
# 3. Redis
redis:
image: redis:6.2
container_name: redis
restart: always
hostname: redis
ports:
- '16379:6379'
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
command: redis-server /usr/local/etc/redis/redis.conf
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 3
networks:
- my-network
# 4. Java 后端
group-buying-sys:
build:
context: ../../.. # 从 docs/tag/v1.0 回到项目根
dockerfile: group-buying-sys-app/Dockerfile
image: smile/group-buying-sys:latest
container_name: group-buying-sys
restart: on-failure
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
ports:
- '8091:8091'
environment:
- TZ=PRC
- SERVER_PORT=8091
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=123456
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/big_market?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai&useSSL=false
- SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
- SPRING_HIKARI_POOL_NAME=Retail_HikariCP
- REDIS_SDK_CONFIG_HOST=redis
- REDIS_SDK_CONFIG_PORT=6379
volumes:
- ./log:/data/log
logging:
driver: json-file
options:
max-size: '10m'
max-file: '3'
networks:
- my-network
# 5. phpMyAdmin只有在 webui profile 下才启动)
phpmyadmin:
image: phpmyadmin:5.2.1
container_name: phpmyadmin
hostname: phpmyadmin
depends_on:
mysql:
condition: service_healthy
ports:
- '8899:80'
environment:
- PMA_HOST=mysql
- PMA_PORT=3306
- MYSQL_ROOT_PASSWORD=123456
networks:
- my-network
profiles: ["webui"]
# 6. Redis Commander只有在 webui profile 下才启动)
redis-admin:
image: spryker/redis-commander:0.8.0
container_name: redis-admin
hostname: redis-commander
restart: always
depends_on:
redis:
condition: service_healthy
ports:
- '8081:8081'
environment:
- REDIS_HOSTS=local:redis:6379
- HTTP_USER=admin
- HTTP_PASSWORD=admin
networks:
- my-network
profiles: ["webui"]

View File

@ -0,0 +1,24 @@
[client]
port = 3306
default-character-set = utf8mb4
[mysqld]
user = mysql
port = 3306
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default-storage-engine = InnoDB
default-authentication-plugin = mysql_native_password
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'
slow_query_log
#long_query_time = 3
slow-query-log-file = /var/log/mysql/mysql.slow.log
log-error = /var/log/mysql/mysql.error.log
default-time-zone = '+8:00'
[mysql]
default-character-set = utf8mb4

View File

@ -0,0 +1,289 @@
/*
Navicat Premium Data Transfer
Source Server : group_buy
Source Server Type : MySQL
Source Server Version : 80042
Source Host : localhost:13306
Source Schema : group_buying_sys
Target Server Type : MySQL
Target Server Version : 80042
File Encoding : 65001
Date: 07/07/2025 16:48:53
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for crowd_tags
-- ----------------------------
DROP TABLE IF EXISTS `crowd_tags`;
CREATE TABLE `crowd_tags` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`tag_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '人群ID',
`tag_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '人群名称',
`tag_desc` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '人群描述',
`statistics` int NOT NULL COMMENT '人群标签统计量',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_tag_id`(`tag_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '人群标签' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of crowd_tags
-- ----------------------------
INSERT INTO `crowd_tags` VALUES (1, 'RQ_KJHKL98UU78H66554GFDV', '潜在消费用户', '潜在消费用户', 28, '2025-06-26 09:12:22', '2025-06-28 11:02:00');
-- ----------------------------
-- Table structure for crowd_tags_detail
-- ----------------------------
DROP TABLE IF EXISTS `crowd_tags_detail`;
CREATE TABLE `crowd_tags_detail` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`tag_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '人群ID',
`user_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_tag_user`(`tag_id` ASC, `user_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '人群标签明细' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of crowd_tags_detail
-- ----------------------------
INSERT INTO `crowd_tags_detail` VALUES (20, 'RQ_KJHKL98UU78H66554GFDV', 'zy123', '2025-06-28 10:53:23', '2025-06-28 10:53:23');
INSERT INTO `crowd_tags_detail` VALUES (21, 'RQ_KJHKL98UU78H66554GFDV', 'smile', '2025-06-28 10:53:23', '2025-06-28 10:53:23');
-- ----------------------------
-- Table structure for crowd_tags_job
-- ----------------------------
DROP TABLE IF EXISTS `crowd_tags_job`;
CREATE TABLE `crowd_tags_job` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`tag_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '标签ID',
`batch_id` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '批次ID',
`tag_type` tinyint(1) NOT NULL DEFAULT 1 COMMENT '标签类型(参与量、消费金额)',
`tag_rule` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '标签规则(限定类型 N次',
`stat_start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计数据,开始时间',
`stat_end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计数据,结束时间',
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态0初始、1计划进入执行阶段、2重置、3完成',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_batch_id`(`batch_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '人群标签任务' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of crowd_tags_job
-- ----------------------------
INSERT INTO `crowd_tags_job` VALUES (1, 'RQ_KJHKL98UU78H66554GFDV', '10001', 0, '100', '2025-06-26 09:13:31', '2025-06-26 09:13:31', 0, '2025-06-26 09:13:31', '2025-06-26 09:13:31');
-- ----------------------------
-- Table structure for group_buy_activity
-- ----------------------------
DROP TABLE IF EXISTS `group_buy_activity`;
CREATE TABLE `group_buy_activity` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增',
`activity_id` bigint NOT NULL COMMENT '活动ID',
`activity_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '活动名称',
`discount_id` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '折扣ID',
`group_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '拼团方式0自动成团、1达成目标拼团',
`take_limit_count` int NOT NULL DEFAULT 1 COMMENT '拼团次数限制',
`target` int NOT NULL DEFAULT 1 COMMENT '拼团目标',
`valid_time` int NOT NULL DEFAULT 15 COMMENT '拼团时长(分钟)',
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '活动状态0创建、1生效、2过期、3废弃',
`start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '活动开始时间',
`end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '活动结束时间',
`tag_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '人群标签规则标识',
`tag_scope` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '人群标签规则范围多选1可见限制、2参与限制',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_activity_id`(`activity_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '拼团活动' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of group_buy_activity
-- ----------------------------
INSERT INTO `group_buy_activity` VALUES (1, 100123, '测试活动', '25120207', 0, 1, 3, 60, 1, '2025-06-19 10:19:40', '2025-07-31 10:19:40', null, null, '2025-06-19 10:19:40', '2025-07-03 18:25:55');
INSERT INTO `group_buy_activity` VALUES (2, 100124, '测试活动2', '25120207', 0, 1, 1, 60, 1, '2025-07-05 13:39:52', '2025-09-28 13:39:34', null, null, '2025-07-05 13:39:52', '2025-07-05 13:40:06');
-- ----------------------------
-- Table structure for group_buy_discount
-- ----------------------------
DROP TABLE IF EXISTS `group_buy_discount`;
CREATE TABLE `group_buy_discount` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`discount_id` int NOT NULL COMMENT '折扣ID',
`discount_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '折扣标题',
`discount_desc` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '折扣描述',
`discount_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '折扣类型0:base、1:tag',
`market_plan` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ZJ' COMMENT '营销优惠计划ZJ:直减、MJ:满减、ZK:折扣、N元购',
`market_expr` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '营销优惠表达式',
`tag_id` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '人群标签,特定优惠限定',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_discount_id`(`discount_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '折扣配置' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of group_buy_discount
-- ----------------------------
INSERT INTO `group_buy_discount` VALUES (1, 25120207, '直减优惠20元', '直减优惠20元', 0, 'ZJ', '20', NULL, '2025-06-25 14:02:13', '2025-06-25 14:02:13');
INSERT INTO `group_buy_discount` VALUES (2, 25120208, '满减优惠100-10元', '满减优惠100-10元', 0, 'MJ', '100,10', NULL, '2025-06-25 14:02:13', '2025-06-25 14:02:13');
INSERT INTO `group_buy_discount` VALUES (4, 25120209, '折扣优惠8折', '折扣优惠8折', 0, 'ZK', '0.8', NULL, '2025-06-25 14:02:13', '2025-06-25 14:02:13');
INSERT INTO `group_buy_discount` VALUES (5, 25120210, 'N元购买优惠', 'N元购买优惠', 0, 'N', '1.99', NULL, '2025-06-25 14:02:13', '2025-06-25 14:02:13');
-- ----------------------------
-- Table structure for group_buy_order
-- ----------------------------
DROP TABLE IF EXISTS `group_buy_order`;
CREATE TABLE `group_buy_order` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`team_id` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '拼单组队ID',
`activity_id` bigint NOT NULL COMMENT '活动ID',
`source` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '渠道',
`channel` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源',
`original_price` decimal(8, 2) NOT NULL COMMENT '原始价格',
`deduction_price` decimal(8, 2) NOT NULL COMMENT '折扣金额',
`pay_price` decimal(8, 2) NOT NULL COMMENT '支付价格',
`target_count` int NOT NULL COMMENT '目标数量',
`complete_count` int NOT NULL COMMENT '完成数量',
`lock_count` int NOT NULL COMMENT '锁单数量',
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态0-拼单中、1-完成、2-失败)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`valid_start_time` datetime NOT NULL COMMENT '拼团开始时间',
`valid_end_time` datetime NOT NULL COMMENT '拼团结束时间',
`notify_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '回调地址',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_team_id`(`team_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '拼团订单表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of group_buy_order
-- ----------------------------
INSERT INTO `group_buy_order` VALUES (8, '90158161', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:46', '2025-07-07 16:34:46', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (9, '19145860', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:46', '2025-07-07 16:34:46', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (10, '70182334', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:46', '2025-07-07 16:34:46', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (11, '60650637', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (12, '97514643', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (13, '02159215', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (14, '84484715', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (15, '47124351', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
INSERT INTO `group_buy_order` VALUES (16, '82549363', 100123, 's01', 'c01', 100.00, 20.00, 80.00, 3, 0, 1, 0, '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '2025-07-07 17:34:47', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify');
-- ----------------------------
-- Table structure for group_buy_order_list
-- ----------------------------
DROP TABLE IF EXISTS `group_buy_order_list`;
CREATE TABLE `group_buy_order_list` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID',
`team_id` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '拼单组队ID',
`order_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单ID',
`activity_id` bigint NOT NULL COMMENT '活动ID',
`start_time` datetime NOT NULL COMMENT '活动开始时间',
`end_time` datetime NOT NULL COMMENT '活动结束时间',
`goods_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品ID',
`source` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '渠道',
`channel` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源',
`original_price` decimal(8, 2) NOT NULL COMMENT '原始价格',
`deduction_price` decimal(8, 2) NOT NULL COMMENT '折扣金额',
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态0初始锁定、1消费完成',
`out_trade_no` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '外部交易单号-确保外部调用唯一幂等',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`biz_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务唯一ID',
`out_trade_time` datetime NULL DEFAULT NULL COMMENT '外部交易时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_order_id`(`order_id` ASC) USING BTREE,
INDEX `idx_user_id_activity_id`(`user_id` ASC, `activity_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '拼团订单明细表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of group_buy_order_list
-- ----------------------------
INSERT INTO `group_buy_order_list` VALUES (17, 'smile01', '90158161', '370749711302', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '915501586360', '2025-07-07 16:34:46', '2025-07-07 16:34:46', '100123_smile01_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (18, 'smile02', '19145860', '579584267705', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '281937053290', '2025-07-07 16:34:46', '2025-07-07 16:34:46', '100123_smile02_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (19, 'smile03', '70182334', '847661746282', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '379287705124', '2025-07-07 16:34:46', '2025-07-07 16:34:46', '100123_smile03_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (20, 'smile04', '60650637', '461093784717', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '255698706695', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '100123_smile04_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (21, 'smile05', '97514643', '573505795059', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '815692003652', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '100123_smile05_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (22, 'smile06', '02159215', '340085487395', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '141413367243', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '100123_smile06_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (23, 'smile07', '84484715', '008099967739', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '000328929417', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '100123_smile07_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (24, 'smile08', '47124351', '400938346390', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '669593424363', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '100123_smile08_1', NULL);
INSERT INTO `group_buy_order_list` VALUES (25, 'smile09', '82549363', '964821189731', 100123, '2025-06-19 10:19:40', '2025-07-31 10:19:40', '9890001', 's01', 'c01', 100.00, 20.00, 0, '885248112084', '2025-07-07 16:34:47', '2025-07-07 16:34:47', '100123_smile09_1', NULL);
-- ----------------------------
-- Table structure for notify_task
-- ----------------------------
DROP TABLE IF EXISTS `notify_task`;
CREATE TABLE `notify_task` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`activity_id` bigint NOT NULL COMMENT '活动ID',
`team_id` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '拼单组队ID',
`notify_url` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '回调接口',
`notify_count` int NOT NULL COMMENT '回调次数',
`notify_status` tinyint(1) NOT NULL COMMENT '回调状态【0初始、1完成、2重试、3失败】',
`parameter_json` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '参数对象',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_team_id`(`team_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of notify_task
-- ----------------------------
INSERT INTO `notify_task` VALUES (1, 100123, '26432069', '暂无', 1, 1, '{\"teamId\":\"26432069\",\"outTradeNoList\":[\"052609289877\",\"621314246005\",\"192426946598\"]}', '2025-07-02 19:34:43', '2025-07-05 13:30:30');
INSERT INTO `notify_task` VALUES (2, 100124, '43505978', 'http://127.0.0.1:8091/api/v1/test/group_buy_notify', 1, 1, '{\"teamId\":\"43505978\",\"outTradeNoList\":[\"451247062978\"]}', '2025-07-05 13:46:16', '2025-07-05 13:46:16');
-- ----------------------------
-- Table structure for sc_sku_activity
-- ----------------------------
DROP TABLE IF EXISTS `sc_sku_activity`;
CREATE TABLE `sc_sku_activity` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`source` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '渠道',
`channel` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源',
`activity_id` bigint NOT NULL COMMENT '活动ID',
`goods_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_sc_goodsid`(`source` ASC, `channel` ASC, `goods_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '渠道商品活动配置关联表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of sc_sku_activity
-- ----------------------------
INSERT INTO `sc_sku_activity` VALUES (1, 's01', 'c01', 100123, '9890001', '2025-06-26 17:15:54', '2025-06-26 17:15:54');
-- ----------------------------
-- Table structure for sku
-- ----------------------------
DROP TABLE IF EXISTS `sku`;
CREATE TABLE `sku` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`source` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '渠道',
`channel` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源',
`goods_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品ID',
`goods_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品名称',
`original_price` decimal(10, 2) NOT NULL COMMENT '商品价格',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_goods_id`(`goods_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '商品信息' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of sku
-- ----------------------------
INSERT INTO `sku` VALUES (1, 's01', 'c01', '9890001', '《手写MyBatis渐进式源码实践》', 100.00, '2025-06-22 11:10:06', '2025-06-22 11:10:06');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,170 @@
/* ========== 全局 ========== */
*{
margin:0;padding:0;box-sizing:border-box;
font-family:'PingFang SC','Helvetica Neue',Arial,sans-serif;
}
body{
background:#f5f5f5;
color:#333;
max-width:500px;
margin:0 auto;
position:relative;
padding-bottom:30px;
}
/* ========== 轮播图 ========== */
.swiper-container{
width:100%;height:375px;position:relative;overflow:hidden;
}
.swiper-wrapper{display:flex;transition:transform .3s;}
.swiper-slide{flex:0 0 100%;height:375px;}
.swiper-slide img{width:100%;height:100%;object-fit:contain;background:#fff;}
.swiper-pagination{
position:absolute;bottom:10px;left:50%;
transform:translateX(-50%);display:flex;gap:6px;
}
.swiper-dot{
width:8px;height:8px;border-radius:50%;
background:rgba(255,255,255,.5);transition:all .3s;
}
.swiper-dot.active{background:#ff5000;width:16px;border-radius:4px;}
/* ========== 商品信息 ========== */
.product-info{background:#fff;padding:15px;margin-bottom:10px;}
.price-row{display:flex;align-items:center;margin-bottom:12px;}
.current-price{color:#ff5000;font-size:28px;font-weight:bold;}
.current-price::before{content:"¥";font-size:18px;}
.original-price{
color:#999;font-size:16px;text-decoration:line-through;margin-left:8px;
}
.original-price::before{content:"¥";}
.title{font-size:18px;font-weight:bold;line-height:1.4;margin-bottom:10px;}
/* 促销行 */
.promo-row{display:flex;align-items:center;gap:6px;margin-top:6px;}
.promo-tag{
flex-shrink:0;
display:inline-block;
background:linear-gradient(90deg,#ff2c2c,#ff6b22);
color:#fff;font-size:12px;padding:2px 6px;border-radius:2px;
}
.promo-box{
display:inline-block;font-size:13px;padding:2px 6px;
border-radius:4px;font-weight:600;line-height:1.2;white-space:nowrap;
}
.promo-box.drop,
.promo-box.sold{
background:linear-gradient(90deg,#ff7e00,#ff5000);
color:#fff;
}
/* “xx人在抢” */
.group-left{margin-right:4px;color:#ff5000;font-weight:bold;}
/* ========== 火焰小图标行 ========== */
.promo-info{color:#ff5000;font-size:14px;margin:8px 0;display:flex;align-items:center;}
.promo-info i{margin-right:5px;}
/* ========== 拼单列表 ========== */
.group-buying{background:#fff;padding:15px;margin-bottom:10px;position:relative;overflow:hidden;}
.section-title{
font-size:16px;font-weight:bold;margin-bottom:12px;position:relative;padding-left:10px;
}
.section-title::before{
content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);
width:3px;height:16px;background:#ff5000;border-radius:2px;
}
.group-users{height:120px;position:relative;overflow:hidden;}
.user-list{position:absolute;top:0;left:0;width:100%;transition:transform .5s ease;}
.user-item{
display:flex;align-items:center;padding:8px 0;border-bottom:1px solid #f5f5f5;
}
.user-item:last-child{border-bottom:none;}
.user-avatar{
width:40px;height:40px;border-radius:50%;background:#f5f5f5;
display:flex;align-items:center;justify-content:center;margin-right:10px;
color:#999;font-size:20px;
}
.user-info{flex:1;}
.user-name{font-size:15px;font-weight:bold;margin-bottom:4px;}
.user-status{font-size:13px;color:#666;}
.left-num{color:#666;margin-right:4px;font-weight:bold;}
.countdown{
display:inline-block;background:#ff5000;color:#fff;
padding:1px 4px;border-radius:2px;margin-left:5px;
}
.buy-btn{
background:linear-gradient(90deg,#ff2c2c,#ff6b22);
color:#fff;border:none;border-radius:4px;padding:6px 15px;
font-size:14px;font-weight:bold;cursor:pointer;
}
/* ========== 底部操作栏 ========== */
.action-bar{
position:fixed;inset-inline:0;bottom:0;max-width:500px;margin:0 auto;
background:#fff;display:flex;height:60px;
box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100;
}
/* 左侧 3 个图标按钮 */
.action-btn{
flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
font-size:12px;color:#666;
}
.action-btn i{font-size:20px;margin-bottom:4px;}
/* 右侧购买区域 */
.purchase-btn{flex:2;display:flex;}
/* 公共:双行按钮布局 */
.btn-single,
.btn-group{
flex:1;
/* 纵向排布价格与文案 */
display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:2px;
border:none;cursor:pointer;
}
/* 各自背景色保持不变 */
.btn-single{background:#ff9500;color:#fff;}
.btn-group {background:#ff5000;color:#fff;}
/* 价格行 */
.btn-price{
font-size:18px;
font-weight:700;
line-height:1;
}
/* 文字行 */
.btn-label{
font-size:12px;
line-height:1;
}
/* ========== 支付弹窗 ========== */
.pay-mask{
position:fixed;inset:0;z-index:999;background:rgba(0,0,0,.45);
display:none;align-items:center;justify-content:center;
}
.pay-box{
width:92%;max-width:420px;background:#fff;border-radius:10px;
padding:28px 24px;text-align:center;
}
.pay-title{margin:0 0 12px;font-size:20px;font-weight:600;}
.pay-amount{margin-bottom:20px;font-size:16px;color:#666;}
.qr-code{
width:260px;max-width:100%;height:auto;display:block;margin:0 auto 32px;
border-radius:12px;object-fit:contain;
}
.pay-btns{display:flex;gap:16px;justify-content:center;}
.btn-primary,
.btn-secondary{
flex:1;padding:12px 0;border:none;border-radius:10px;
font-size:16px;font-weight:600;cursor:pointer;
}
.btn-primary{background:#12a400;color:#fff;}
.btn-secondary{background:#f0f0f0;}

View File

@ -0,0 +1,91 @@
/* 整体布局 */
*{margin:0;padding:0;box-sizing:border-box;font-family:'PingFang SC','Helvetica Neue',Arial,sans-serif;}
body{
height:100vh;
display:flex;
align-items:center;
justify-content:center;
background:#d5d1e8; /* 柔和紫色背景 */
}
.container{
width:90%;max-width:420px;
}
.login-form{
background:#fff;
padding:36px 28px;
border-radius:14px;
box-shadow:0 4px 12px rgba(0,0,0,.08);
}
.login-form h2{
font-size:24px;
font-weight:600;
text-align:center;
margin-bottom:32px;
}
/* 输入框组 */
.input-group{
position:relative;
margin-bottom:26px;
}
.input-group input{
width:100%;
padding:14px 16px;
font-size:16px;
border:2px solid #aaa3;
border-radius:10px;
outline:none;
transition:border .25s;
}
.input-group input:focus{
border-color:#1296ff;
}
.input-group label{
position:absolute;
left:18px;
top:50%;
transform:translateY(-50%);
color:#999;
pointer-events:none;
transition:all .25s;
}
/* 上浮效果 */
.input-group input:focus + label,
.input-group input:not(:placeholder-shown) + label{
top:0;
transform:translateY(-50%) scale(.86);
background:#fff;
padding:0 4px;
color:#1296ff;
}
/* 登录按钮 */
button[type="submit"]{
width:100%;
padding:14px 0;
font-size:18px;
font-weight:600;
color:#fff;
background:#1296ff;
border:none;
border-radius:10px;
cursor:pointer;
transition:opacity .25s;
}
button[type="submit"]:hover{opacity:.9;}
.error-message{
margin-top:18px;
font-size:14px;
color:#e02424;
text-align:center;
display:none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

View File

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>手写 MyBatis渐进式源码实践 - 拼多多</title>
<!-- 现成样式 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<link rel="stylesheet" href="css/index.css" />
</head>
<body>
<!-- 顶部轮播图 -->
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide"><img src="images/goods_info2.png" /></div>
<div class="swiper-slide"><img src="images/goods_info3.png" /></div>
<div class="swiper-slide"><img src="images/goods_info1.png" /></div>
</div>
<div class="swiper-pagination"></div>
</div>
<!-- 商品信息 -->
<div class="product-info">
<div class="price-row">
<div class="current-price" id="currentPrice"></div>
<div class="original-price" id="originalPrice"></div>
</div>
<!-- 标题写死即可,如需从接口取也可在 JS 中替换 -->
<div class="title" id="goodsTitle">手写 MyBatis渐进式源码实践全彩</div>
<div class="promo-row">
<span class="promo-tag">大促优惠</span>
<span class="promo-box drop" id="dropPrice"></span>
<span class="promo-box sold" id="soldBox"></span>
</div>
</div>
<!-- 拼单区域 -->
<div class="group-buying">
<div class="section-title" id="groupTitle"></div>
<div class="group-users">
<!-- 列表由 JS 动态注入 -->
<div class="user-list" id="userList"></div>
</div>
</div>
<!-- 底部操作栏 -->
<div class="action-bar">
<div class="action-btn">
<i class="fas fa-home"></i><span>首页</span>
</div>
<div class="action-btn">
<i class="fas fa-heart"></i><span>收藏</span>
</div>
<div class="action-btn">
<i class="fas fa-shopping-cart"></i><span>购物车</span>
</div>
<div class="purchase-btn">
<button class="btn-single" id="btnSingle" data-price="">
<span class="btn-price" id="singlePrice"></span>
<span class="btn-label">单独购买</span>
</button>
<button class="btn-group" id="btnGroup" data-price="">
<span class="btn-price" id="groupPrice"></span>
<span class="btn-label">开团购买</span>
</button>
</div>
</div>
<!-- 支付弹窗 -->
<div id="paymentModal" class="pay-mask">
<div class="pay-box">
<h2 class="pay-title">请扫码支付</h2>
<p class="pay-amount" id="paymentAmount"></p>
<img src="images/qrcode.png" alt="支付二维码" class="qr-code" />
<div class="pay-btns">
<button id="cancelPayment" class="btn-secondary">取消支付</button>
<button id="completePayment" class="btn-primary">支付完成</button>
</div>
</div>
</div>
<!-- 逻辑脚本 -->
<script src="js/index.js"></script>
</body>
</html>

View File

@ -0,0 +1,253 @@
/* -------------------------------------------------------
* Author :
* Desc : 改进版拼团页面脚本userId cookie 里读
* ----------------------------------------------------- */
document.addEventListener('DOMContentLoaded', () => {
/* ========== 通用工具 ========== */
const getCookie = (k) =>
document.cookie
.split(';')
.map((c) => c.trim())
.find((c) => c.startsWith(k + '='))?.split('=')[1] || null;
/* ----------- 0. DOM 快捷引用 ----------- */
const $ = (id) => document.getElementById(id);
const currentPrice = $('currentPrice');
const originalPriceElem = $('originalPrice');
const dropPrice = $('dropPrice');
const soldBox = $('soldBox');
const groupTitle = $('groupTitle');
const userList = $('userList');
const singlePriceSpan = $('singlePrice');
const groupPriceSpan = $('groupPrice');
const btnSingle = $('btnSingle');
const btnGroup = $('btnGroup');
/* =====================================================
* 1. 取接口数据并渲染
* =================================================== */
const API_URL = 'http://124.71.159.195:8091/api/v1/gbm/index/query_group_buy_market_config';
// 读取 cookie 中的 username 当作 userId
const username = getCookie('username');
// 如果没登录,直接跳去登录页,免得后面接口 401/判空
if (!username) {
location.href = 'login.html';
return;
}
const POST_BODY = {
userId : username, // 不再写死
source : 's01',
channel: 'c01',
goodsId: '9890001'
};
fetch(API_URL, {
method : 'POST',
headers: { 'Content-Type': 'application/json' },
body : JSON.stringify(POST_BODY),
})
.then((r) => r.json())
.then(({ code, info, data }) => {
if (code !== '0000' || !data) {
console.error('接口异常:', info);
return;
}
renderGoods(data.goods);
renderStatistic(data.teamStatistic);
renderTeams(data.teamList, data.goods?.payPrice);
})
.catch((e) => console.error('接口请求失败:', e));
/* ------------- 渲染商品信息 ------------- */
function renderGoods(g = {}) {
const { originalPrice = 0, payPrice = 0, deductionPrice = 0 } = g;
currentPrice.textContent = payPrice;
originalPriceElem.textContent = originalPrice;
dropPrice.textContent = `直降 ¥${deductionPrice}`;
singlePriceSpan.textContent = `${originalPrice}`;
groupPriceSpan.textContent = `${payPrice}`;
btnSingle.dataset.price = originalPrice;
btnGroup.dataset.price = payPrice;
}
/* ------------- 渲染统计信息 ------------- */
function renderStatistic(stat = {}) {
const { allTeamUserCount = 0 } = stat;
groupTitle.textContent = `${allTeamUserCount}人在抢,参与可立即拼成`;
soldBox.textContent = `${allTeamUserCount}人再抢`;
}
/* ------------- 渲染拼团列表 ------------- */
function renderTeams(list = [], groupPrice = 0) {
if (!list || list.length === 0) {
groupTitle.textContent = '小伙伴,赶紧去开团吧,做村里最靓的仔。';
return;
}
userList.innerHTML = '';
list.forEach((t) => userList.appendChild(makeItem(t, groupPrice)));
initUserMarquee();
initCountdown();
}
function makeItem(team, price) {
const { userId, targetCount, lockCount, validTimeCountdown } = team;
const leftNum = Math.max(targetCount - lockCount, 0);
const timeText = validTimeCountdown || '00:00:00';
const div = document.createElement('div');
div.className = 'user-item';
div.innerHTML = `
<div class="user-avatar"><i class="fas fa-user"></i></div>
<div class="user-info">
<div class="user-name">${userId}</div>
<div class="user-status">
仅剩${leftNum}人成团
<span class="countdown">${timeText}</span>
</div>
</div>
<button class="buy-btn" data-price="${price}">参与拼团</button>
`;
return div;
}
/* =====================================================
* 2. 拼单列表纵向轮播
* =================================================== */
function initUserMarquee() {
const items = userList.querySelectorAll('.user-item');
if (items.length <= 1) return;
const itemH = items[0].offsetHeight;
userList.appendChild(items[0].cloneNode(true)); // 无缝衔接
let idx = 0;
userList.addEventListener('transitionend', () => {
if (idx >= items.length) {
userList.style.transition = 'none';
userList.style.transform = 'translateY(0)';
idx = 0;
void userList.offsetWidth;
}
});
setInterval(() => {
idx++;
userList.style.transition = 'transform .5s ease';
userList.style.transform = `translateY(${-idx * itemH}px)`;
}, 3000);
}
/* =====================================================
* 3. 倒计时
* =================================================== */
let countdownData = [];
function initCountdown() {
const els = document.querySelectorAll('.countdown');
countdownData = Array.from(els).map((el) => ({
el,
remain: toSec(el.textContent.trim()),
}));
setInterval(tick, 1000);
}
const toSec = (t) => {
if (!t.includes(':')) return 0;
const [h = '00', m = '00', s = '00'] = t.split(':');
return +h * 3600 + +m * 60 + +s;
};
const fmt = (n) => String(n).padStart(2, '0');
const format = (s) => `${fmt(s / 3600 | 0)}:${fmt((s % 3600) / 60 | 0)}:${fmt(s % 60)}`;
function tick() {
countdownData.forEach((c) => {
if (c.remain > 0) {
c.remain--;
c.el.textContent = format(c.remain);
if (c.remain === 0) expire(c.el);
}
});
}
function expire(el) {
el.textContent = '00:00:00';
const item = el.closest('.user-item');
item?.classList.add('expired');
item?.querySelector('.buy-btn')?.setAttribute('disabled', 'disabled');
}
/* =====================================================
* 4. 支付弹窗事件委托
* =================================================== */
const modal = $('paymentModal');
const amountText = $('paymentAmount');
const cancelPayment = $('cancelPayment');
const completePayment= $('completePayment');
document.body.addEventListener('click', (e) => {
const btn = e.target.closest('.buy-btn, .btn-single, .btn-group');
if (!btn) return;
// 再次确认 cookie防止手动删 cookie
if (!getCookie('username')) {
location.href = 'login.html';
return;
}
amountText.textContent = `支付金额 ¥${btn.dataset.price || 0}`;
modal.style.display = 'flex';
});
cancelPayment.onclick = () => modal.style.display = 'none';
completePayment.onclick= () => { alert('支付成功!'); modal.style.display = 'none'; };
modal.addEventListener('click', (e) => { if (e.target === modal) modal.style.display = 'none'; });
/* =====================================================
* 5. 顶部横向轮播原逻辑保留
* =================================================== */
const wrapper = document.querySelector('.swiper-wrapper');
const slides = [...wrapper.children];
const pagination = document.querySelector('.swiper-pagination');
const count = slides.length;
let current = 0, startX = 0, dragging = false, timer;
for (let i = 0; i < count; i++) {
const dot = document.createElement('div');
dot.className = 'swiper-dot' + (i === 0 ? ' active' : '');
dot.onclick = () => goTo(i);
pagination.appendChild(dot);
}
const dots = pagination.children;
const goTo = (i) => {
current = (i + count) % count;
wrapper.style.transition = 'transform .3s ease';
wrapper.style.transform = `translateX(-${current * 100}%)`;
[...dots].forEach((d, j) => d.classList.toggle('active', j === current));
};
const auto = () => { timer = setInterval(() => goTo(current + 1), 3000); };
const stop = () => clearInterval(timer);
auto();
const getX = (e) => (e.touches ? e.touches[0].clientX : e.clientX);
wrapper.addEventListener('pointerdown', (e) => { stop(); dragging = true; startX = getX(e); wrapper.style.transition = 'none'; });
wrapper.addEventListener('pointermove', (e) => { if (!dragging) return; const diff = getX(e) - startX; wrapper.style.transform = `translateX(calc(${-current * 100}% + ${diff}px))`; });
wrapper.addEventListener('pointerup', endSwipe);
wrapper.addEventListener('pointercancel',endSwipe);
wrapper.addEventListener('pointerleave', endSwipe);
function endSwipe(e) {
if (!dragging) return;
dragging = false;
const diff = getX(e) - startX;
const limit = wrapper.offsetWidth * 0.15;
if (diff > limit) goTo(current - 1);
else if (diff < -limit) goTo(current + 1);
else goTo(current);
auto();
}
});

View File

@ -0,0 +1,28 @@
document.addEventListener('DOMContentLoaded',()=>{
const loginForm = document.getElementById('loginForm');
const errorMessage = document.getElementById('errorMessage');
loginForm.addEventListener('submit',e=>{
e.preventDefault();
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value.trim();
if(!username || !password){
errorMessage.textContent = '用户名和密码不能为空';
errorMessage.style.display = 'block';
return;
}
/* 这里可替换为真实校验逻辑 —— 目前直接视为成功 */
errorMessage.style.display = 'none';
/* 写入 cookie1 天有效 */
const expire = new Date();
expire.setDate(expire.getDate() + 1);
document.cookie = `username=${encodeURIComponent(username)}; expires=${expire.toUTCString()}; path=/`;
/* 登录后跳回首页(商品详情页) */
window.location.href = 'index.html';
});
});

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
<title>欢迎登录 - 宇哥拼团</title>
<link rel="stylesheet" href="css/login.css"/>
</head>
<body>
<div class="container">
<form id="loginForm" class="login-form">
<h2>欢迎登录 - 宇哥拼团</h2>
<div class="input-group">
<input type="text" id="username" required/>
<label for="username">用户名</label>
</div>
<div class="input-group">
<input type="password" id="password" required/>
<label for="password">密码</label>
</div>
<button type="submit">登录</button>
<p class="error-message" id="errorMessage"></p>
</form>
</div>
<script src="js/login.js"></script>
</body>
</html>

View File

@ -1,5 +1,3 @@
spring:
config:
name: group-buying-sys-app
profiles:
active: dev
active: prod

View File

@ -75,7 +75,7 @@
select team_id, activity_id, target_count, complete_count, lock_count, status, valid_start_time, valid_end_time,
notify_url
from group_buy_order
where status = 0 and target_count > lock_count and team_id in
where status = 0 and target_count > lock_count and valid_end_time > now() and team_id in
<foreach item="teamId" collection="teamIds" open="(" separator="," close=")">
#{teamId}
</foreach>