diff --git a/docs/dev-ops/mysql/sql/0716paymall.sql b/docs/dev-ops/mysql/sql/0716paymall.sql
index 85d7701..52cfb98 100644
--- a/docs/dev-ops/mysql/sql/0716paymall.sql
+++ b/docs/dev-ops/mysql/sql/0716paymall.sql
@@ -13,6 +13,16 @@
Date: 16/07/2025 17:08:44
*/
+-- 如果存在就删除
+DROP DATABASE IF EXISTS `pay-mall`;
+
+-- 创建(如需指定字符集与排序规则,可自行调整)
+CREATE DATABASE IF NOT EXISTS `group_buying_sys`
+ DEFAULT CHARACTER SET utf8mb4
+ COLLATE utf8mb4_unicode_ci;
+
+-- 切换到该库
+USE `pay-mall`;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
diff --git a/docs/dev-ops/nginx/html/css/index.css b/docs/dev-ops/nginx/html/css/index.css
index 7ec93be..f35e1f4 100644
--- a/docs/dev-ops/nginx/html/css/index.css
+++ b/docs/dev-ops/nginx/html/css/index.css
@@ -14,7 +14,7 @@ body{
/* ========== 轮播图 ========== */
.swiper-container{
- width:100%;height:375px;position:relative;overflow:hidden;
+ width:100%;height:375px;position:relative;overflow:hidden;margin-top: 10px;
}
.swiper-wrapper{display:flex;transition:transform .3s;}
.swiper-slide{flex:0 0 100%;height:375px;}
@@ -62,7 +62,14 @@ body{
}
/* ========== 拼单列表 ========== */
-.group-buying{background:#fff;padding:15px;margin-bottom:10px;position:relative;overflow:hidden;}
+.group-buying {
+ background: #fff;
+ padding: 15px;
+ margin-bottom: 10px; /* 如果还想保留一点外边距 */
+ min-height: 230px; /* 根据需要调整数值 */
+ position: relative;
+ overflow: hidden;
+}
.section-title{
font-size:16px;font-weight:bold;margin-bottom:12px;position:relative;padding-left:10px;
}
@@ -71,7 +78,7 @@ body{
width:3px;height:16px;background:#ff5000;border-radius:2px;
}
-.group-users{height:120px;position:relative;overflow:hidden;}
+.group-users{height:180px;position:relative;overflow:hidden;}
.user-list{position:absolute;top:0;left:0;width:100%;transition:transform .5s ease;}
.user-item{
diff --git a/docs/dev-ops/nginx/html/js/index.js b/docs/dev-ops/nginx/html/js/index.js
index 387a64e..e899bf2 100644
--- a/docs/dev-ops/nginx/html/js/index.js
+++ b/docs/dev-ops/nginx/html/js/index.js
@@ -25,7 +25,7 @@ document.addEventListener('DOMContentLoaded', () => {
/* =====================================================
* 1. 取接口数据并渲染
* =================================================== */
- const CFG_API = 'http://127.0.0.1:8091/api/v1/gbm/index/query_group_buy_market_config';
+ const CFG_API = '/api/v1/gbm/index/query_group_buy_market_config';
// 登录检查
const loginToken = getCookie('loginToken');
@@ -86,7 +86,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
userList.innerHTML = '';
list.forEach(t => userList.appendChild(makeItem(t, groupPrice)));
- initUserMarquee();
+ initUserMarquee(3); // 显示 3 条
initCountdown();
}
@@ -137,28 +137,61 @@ document.addEventListener('DOMContentLoaded', () => {
/* =====================================================
* 2. 拼单列表纵向轮播
* =================================================== */
- function initUserMarquee() {
- const items = userList.querySelectorAll('.user-item');
- if (items.length <= 1) return;
+ function initUserMarquee(visibleCount = 3, interval = 3000, duration = 500) {
+ const box = document.querySelector('.group-users');
+ const listEl = userList;
+ let originals = Array.from(listEl.children);
+ const total = originals.length;
- const itemH = items[0].offsetHeight;
- userList.appendChild(items[0].cloneNode(true));
+ if (total === 0) return;
+ // 如果原本就 <= 可见数,直接定高,不滚
+ if (total <= visibleCount) {
+ const h0 = originals[0].offsetHeight;
+ box.style.height = (h0 * visibleCount) + 'px';
+ return;
+ }
- let idx = 0;
- userList.addEventListener('transitionend', () => {
- if (idx >= items.length) {
- userList.style.transition = 'none';
- userList.style.transform = 'translateY(0)';
- idx = 0;
- void userList.offsetWidth;
+ // 1. 复制整份加入末尾
+ originals.forEach(item => listEl.appendChild(item.cloneNode(true)));
+
+ // 2. 重新测量单条高度(此时 DOM 完整)
+ const itemH = listEl.children[0].offsetHeight;
+
+ // 3. 设定窗口高度
+ box.style.height = (itemH * visibleCount) + 'px';
+
+ // 4. 状态
+ let index = 0;
+ let ticking = false;
+
+ function step() {
+ index++;
+ listEl.style.transition = `transform ${duration}ms ease`;
+ listEl.style.transform = `translateY(-${index * itemH}px)`;
+ }
+
+ listEl.addEventListener('transitionend', () => {
+ ticking = false;
+ // 5. 到达复制段的“第一帧”(index === total)就无缝重置
+ if (index === total) {
+ listEl.style.transition = 'none';
+ listEl.style.transform = 'translateY(0)';
+ index = 0;
+ // 强制 reflow 再恢复 transition
+ void listEl.offsetHeight;
+ listEl.style.transition = `transform ${duration}ms ease`;
}
});
- setInterval(() => {
- idx++;
- userList.style.transition = 'transform .5s ease';
- userList.style.transform = `translateY(${-idx * itemH}px)`;
- }, 3000);
+ const timer = setInterval(() => {
+ if (!ticking) {
+ ticking = true;
+ step();
+ }
+ }, interval);
+
+ // 可选:鼠标悬停暂停
+ listEl.addEventListener('mouseenter', () => clearInterval(timer));
}
/* =====================================================
@@ -202,8 +235,7 @@ document.addEventListener('DOMContentLoaded', () => {
/* =====================================================
* 4. 支付相关
* =================================================== */
- const PAY_MALL_URL = 'http://127.0.0.1:8092';
- const CREATE_PAY_API = `${PAY_MALL_URL}/api/v1/alipay/create_pay_order`;
+ const CREATE_PAY_API = `/api/v1/alipay/create_pay_order`;
// 4.1 支付确认弹窗
function showPaymentConfirm(price){
diff --git a/pay-mall-app/pom.xml b/pay-mall-app/pom.xml
index e0fa5ad..99d1aa5 100644
--- a/pay-mall-app/pom.xml
+++ b/pay-mall-app/pom.xml
@@ -78,7 +78,10 @@
converter-gson
2.9.0
-
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
edu.whut
diff --git a/pay-mall-app/src/main/java/edu/whut/config/RabbitMQConfig.java b/pay-mall-app/src/main/java/edu/whut/config/RabbitMQConfig.java
new file mode 100644
index 0000000..b0b144c
--- /dev/null
+++ b/pay-mall-app/src/main/java/edu/whut/config/RabbitMQConfig.java
@@ -0,0 +1,31 @@
+package edu.whut.config;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.TopicExchange;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RabbitMQConfig {
+
+ /**
+ * 消费拼团消息
+ */
+ @Bean
+ public Binding topicTeamSuccessBinding(
+ @Value("${spring.rabbitmq.config.consumer.topic_team_success.exchange}") String exchangeName,
+ @Value("${spring.rabbitmq.config.consumer.topic_team_success.routing_key}") String routingKey,
+ @Value("${spring.rabbitmq.config.consumer.topic_team_success.queue}") String queue) {
+
+ // 消息生产方的交换机
+ TopicExchange topicExchange = new TopicExchange(exchangeName, true, false);
+
+ return BindingBuilder.bind(new Queue(queue, true))
+ .to(topicExchange)
+ .with(routingKey);
+ }
+
+}
diff --git a/pay-mall-app/src/main/resources/application-dev.yml b/pay-mall-app/src/main/resources/application-dev.yml
index 593e3d6..ab6497b 100644
--- a/pay-mall-app/src/main/resources/application-dev.yml
+++ b/pay-mall-app/src/main/resources/application-dev.yml
@@ -43,6 +43,28 @@ spring:
connection-timeout: 30000 #数据库连接超时时间,默认30秒,即30000
connection-test-query: SELECT 1
type: com.zaxxer.hikari.HikariDataSource
+ # RabbitMQ
+ rabbitmq:
+ addresses: 192.168.10.218
+ port: 5672
+ username: admin
+ password: admin
+ listener:
+ simple:
+ prefetch: 1 # 每次投递n个消息,消费完在投递n个
+ template:
+ delivery-mode: persistent # 确保全局默认设置为持久化(可选)
+ # 消息配置
+ config:
+ consumer:
+ # 消费 topic 主题,team_success
+ topic_team_success:
+ # 绑定交换机 - 消息提供者的交换机
+ exchange: group_buy_market_exchange
+ # 消息主题
+ routing_key: topic.team_success
+ # 消费队列 - 每个系统有自己的消费队列
+ queue: pay_mall_queue_2_topic_team_success
# MyBatis 配置【如需使用记得打开】
mybatis:
diff --git a/pay-mall-app/src/main/resources/application.yml b/pay-mall-app/src/main/resources/application.yml
index 999d6e4..7f350e7 100644
--- a/pay-mall-app/src/main/resources/application.yml
+++ b/pay-mall-app/src/main/resources/application.yml
@@ -1,3 +1,3 @@
spring:
profiles:
- active: prod,local
+ active: dev,local
diff --git a/pay-mall-trigger/pom.xml b/pay-mall-trigger/pom.xml
index 0862007..62c586b 100644
--- a/pay-mall-trigger/pom.xml
+++ b/pay-mall-trigger/pom.xml
@@ -26,7 +26,10 @@
org.apache.commons
commons-lang3
-
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
edu.whut
diff --git a/pay-mall-trigger/src/main/java/edu/whut/trigger/listener/TeamSuccessTopicListener.java b/pay-mall-trigger/src/main/java/edu/whut/trigger/listener/TeamSuccessTopicListener.java
new file mode 100644
index 0000000..fff155b
--- /dev/null
+++ b/pay-mall-trigger/src/main/java/edu/whut/trigger/listener/TeamSuccessTopicListener.java
@@ -0,0 +1,32 @@
+package edu.whut.trigger.listener;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.ExchangeTypes;
+import org.springframework.amqp.rabbit.annotation.Exchange;
+import org.springframework.amqp.rabbit.annotation.Queue;
+import org.springframework.amqp.rabbit.annotation.QueueBinding;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * 结算完成消息监听
+ */
+@Slf4j
+@Component
+public class TeamSuccessTopicListener {
+
+ /**
+ * 指定消费队列
+ */
+ @RabbitListener(
+ bindings = @QueueBinding(
+ value = @Queue(value = "${spring.rabbitmq.config.consumer.topic_team_success.queue}"),
+ exchange = @Exchange(value = "${spring.rabbitmq.config.consumer.topic_team_success.exchange}", type = ExchangeTypes.TOPIC),
+ key = "${spring.rabbitmq.config.consumer.topic_team_success.routing_key}"
+ )
+ )
+ public void listener(String message) {
+ log.info("接收消息:{}", message);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 5250d11..d882b51 100644
--- a/pom.xml
+++ b/pom.xml
@@ -141,6 +141,12 @@
hutool-all
5.8.26
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+ 3.2.0
+
edu.whut