7.16 小商场UI(拼团的UI复用并修改)与接口对接
This commit is contained in:
parent
d8643194ee
commit
b235462189
@ -11,7 +11,7 @@
|
|||||||
Target Server Version : 80042
|
Target Server Version : 80042
|
||||||
File Encoding : 65001
|
File Encoding : 65001
|
||||||
|
|
||||||
Date: 16/07/2025 13:59:35
|
Date: 16/07/2025 17:08:44
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
@ -40,13 +40,16 @@ CREATE TABLE `pay_order` (
|
|||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
UNIQUE INDEX `uq_order_id`(`order_id` ASC) USING BTREE,
|
UNIQUE INDEX `uq_order_id`(`order_id` ASC) USING BTREE,
|
||||||
INDEX `idx_user_id_product_id`(`user_id` ASC, `product_id` ASC) USING BTREE
|
INDEX `idx_user_id_product_id`(`user_id` ASC, `product_id` ASC) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of pay_order
|
-- Records of pay_order
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
INSERT INTO `pay_order` VALUES (31, 'smile01', '9890001', 'MyBatisBook', '376456387082', '2025-07-16 13:47:35', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=QiS6Ss9WVG45w4G85WPEw8D7TLbahtqfJQ8M%2BtPGI4FGP0njFMLDamSWT%2B5H67mZlEJjO%2F03a0kZ%2Fs4eg2bsX2ZAn8hzFyUPNHIbAfNikjnlWPxVBT1ageB3zmc95xP9r%2BtGRnuPqryxSs1FzKQjqbQJJRWXxdQE%2FEHjq5bc6zTODbiDMk%2B8hhy104FlM%2BPiT4SKKOfAgtHI2rFruW%2B6rlIHdxsI5roHvq650uDQ0Ir%2BqTZ46FP%2Fv2RpNfmOp1pR8%2BJ4NAZlti8wng0AzWVkl7xUdsgBQ%2BD9Bja57Lt8pZC1SI0%2BrV0HOU0H%2F8AkuVvdCzAPgwUrzaPkYexrsH6a2g%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+13%3A47%3A35&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"376456387082","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 13:50:29', 1, 20.00, 80.00, '2025-07-16 13:47:35', '2025-07-16 13:58:39');
|
INSERT INTO `pay_order` VALUES (36, 'opEtBvq6go0co-HQC5DSHkKfkdds', '9890001', 'MyBatisBook', '498873906342', '2025-07-16 15:53:11', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=YHURFlD5GRayv1bHAezkCE0W%2BJbS5l4pxwR2eH9k6SLfes2DozLEVvgo3H28mMNTRw1EK6lc%2FN56HUvBOTD4DNtpQ72d3S4v51v%2FqV1%2FxPHIC9HDAQD%2FTTTln6ZjRzLSkWQxRn7aQOBiVhTplu7B%2BBkPKIpIEwWsKtlI0GSCM7%2BE9dQdKrv9BYLD9eAGIJNhL%2FRC3Fu1CkbvhyMPmCxkjkH1vrbx5tmeeWXfOaMkXmR3vePVE9f4xcZgKHiYcBPdaSO%2Brs4uzskw9idRwUzL1xX%2BZElA2hZGCtgXCYz40MVsWiPiwT0voUQcbFE48ZfobOAzKtmpfgILFF2p3qLoFQ%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+15%3A53%3A11&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"498873906342","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 15:54:03', 1, 20.00, 80.00, '2025-07-16 15:53:11', '2025-07-16 16:44:47');
|
||||||
INSERT INTO `pay_order` VALUES (32, 'smile02', '9890001', 'MyBatisBook', '503529040337', '2025-07-16 13:55:51', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=l1%2FB5WhTnlHDBZGCmFD62mNUQZSGPISW0ny%2F%2Btxju2Mx0m8Ju%2B80cy43YPNGb5Law80EaByVO9O90S5yJ5qIa4%2FMWNHdcrpoVpdaoiuSwa8FvHAZYA65KM6nB7fe1ESy4Q3xOVYi9TS7TFhg5feSbrCe%2F48jfs0A7GxFzZuioQPXb8fFlTpQhm0w4bsUwYf5YfNKxUUNQn%2BK%2FMT2QMBsEceCT0%2FJn5OM2Yl1IufMvdkMsZVQLdN5%2BxsDP%2BabPTxmV%2BGB0gshWT9mHlfRONqxEbqkug99CmqxQKy9GEnKDZFCdkSknAI3ukS14cflpJB84Vq4Pb1m5ZA8AyCbcUUlYQ%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+13%3A55%3A51&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"503529040337","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 13:56:47', 1, 20.00, 80.00, '2025-07-16 13:55:50', '2025-07-16 13:58:39');
|
INSERT INTO `pay_order` VALUES (39, 'smile01', '9890001', 'MyBatisBook', '369511405849', '2025-07-16 16:28:58', 100.00, 'PAY_SUCCESS', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=O11ikCcaT503aUwewmNj71Y6PZ5IsqAQ3vqrJK3bCahFJe9KmWbAYMkO91%2FGrCmkmekZ%2Bl2wL85OS4IPNlAMRKmtOhRC22VyqHFjhFaLCr5zzQjvoWgFWMOiAt33x8cdeLvi1RW7nQ66ll1SEi33syInH154TO3MrqTMmLGIPdMvXDmz%2FyfmSQ5h6ZQdN9aaSdFSTR8q%2BFgcvGMSVJSTAog%2BJbMHwsRxZXqlFI8LX8%2Fsh7fRoM87l8Ws1jAKRm6BS6sRgbfmEOdtJRO95Uc2LYLVT817%2B3Fvll2B8KwkSa66Ar2Oo6s6emCQJl3IG67TcA5%2FdPhtyYFtFsqSbpAqQw%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+16%3A28%3A59&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"369511405849","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 16:30:33', 1, 20.00, 80.00, '2025-07-16 16:28:57', '2025-07-16 16:30:33');
|
||||||
INSERT INTO `pay_order` VALUES (33, 'smile03', '9890001', 'MyBatisBook', '274640446882', '2025-07-16 13:57:47', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=aeJrx00JaoiPo%2Bd%2BelwPekikVqqcT0UjTQluUAWGtxia0Zp9BuzIljU%2BOrQtBj9vhV9cfh1qFxh4%2BGqpTyJN0ZTTPoQa1U4tT1UE2be5JKcV%2BKFtXfpSL%2BFHx7oPLoqXUnhksF5Q8h82n680LVt%2FFGfwo4mBKuFzJ%2FX1libjCzFwdcIHvosESbClSHb5hmNSyWSmyh4UZra9wvaofUjZvrE2%2FhEYIK01Sl9cTXFvmnCHaGLdKgBPUYdx5zVQryKuV%2BfZgDw9s7kEjPommlx3s0XK0tFb8dmitYc%2FdFvVDjDTJ8YE4PuFDedG2qtmOgJ%2BteXfrUHjWwfsMLTuor9elA%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+13%3A57%3A47&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"274640446882","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 13:58:39', 1, 20.00, 80.00, '2025-07-16 13:57:47', '2025-07-16 13:58:40');
|
INSERT INTO `pay_order` VALUES (40, 'smile01', '9890001', 'MyBatisBook', '698481154046', '2025-07-16 16:34:25', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=nCszW8W%2F%2Fyhu7x9uPcJJmKv8gYoayqFmqLojw8Q%2BDt2bI5OqBKyoZhT61rdexukp%2BLtvZcWOxJ8sDN%2Bf4Zq8FUvb7uxCsWaD7HWTlgm5KXdSc%2BEVawudxr8aWd16ZRI69WDCGiAvKCgTkX20me2%2Bnv2sdHBd2p7G%2FdkhMtVpMPQHxu4a%2BCQ%2Fatlb2JU1qT1GK0SGglQjtQQpb9mNJIIS5NTf2rGF5%2BIB%2BGWU%2Fj0tGl5ney6ZhxuEnRJzIbrgz6riYOsvhguntMIZxws1%2F8ZakrGE2MYsjesahI8v6eaNAJij2jjkLqX40jrleFwLGMZvidRPgOnpr4z3q5n%2FEFo5dQ%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+16%3A34%3A25&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"698481154046","total_amount":100.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 16:35:36', 0, 0.00, 100.00, '2025-07-16 16:34:24', '2025-07-16 16:35:36');
|
||||||
|
INSERT INTO `pay_order` VALUES (41, 'smile02', '9890001', 'MyBatisBook', '541129857040', '2025-07-16 16:36:49', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=Ot5z8oK7xO7jmA2pzCnVkE32AdUjurl9YPuSgNwtWjI433Ki5ZAuvhxFfW2V6N20JQPYiGA%2FFYwlgPZ2V53DfeYEDwTZ9yXyJ9ZL74J6kUWGsotnp53JUQZkTTTMT6uBIsl2r2lt5YeLo3GvOkTdxBqxpow%2F%2F8ZxvY6xi9IIN5DGQ8inhwNR8p3rAJS82PKHKFHImNeYg51pUPtU0cqJzrXeN4bT0EincERBhT7j6pcvvRjrPDX9VgSRdwtp5duQC2I9PabqihLVXhAMwhafcKkSRPrYVlJmUgedtYw2Z7Y74CTjgMdYuEKznRsEGvabqUCP2nQ2lWssVTsmJ35cJQ%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+16%3A36%3A49&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"541129857040","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 16:38:05', 1, 20.00, 80.00, '2025-07-16 16:36:49', '2025-07-16 16:44:47');
|
||||||
|
INSERT INTO `pay_order` VALUES (42, 'smile04', '9890001', 'MyBatisBook', '381698719384', '2025-07-16 16:43:47', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=MN1%2F2PVMYwkEB4VE3SKphhG95QQUGlMeOwN5PwfNd1C4rdGo4PWJpBor5G3FfuHknCcugcwwQ09zhAljDG6ZxggkzwPvkh04zv5eWuWfIXXIl%2Bzj5onXv6rVvElzL1BFUJ8UxpdgjNNFVWQJVLndHA0WuAB4ZQ0W9c4Jew%2FMd1Gd3p7HzESC16Wcy1zMxg%2FSUPSjORA9Cmf2jrT1RqDAy2Sj%2FNZriEByMlX0rIz44LFCciDLBVo3JzYmshOoU%2FtDwSLn3Ob9WMlLRaKmae8X4bSzCesYbEvEmlN9jl9Mc3MN4hJ0RZ8%2FpC%2B9xk6%2FNKNW1VROTzg801iLhxUK0vAc0g%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+16%3A43%3A47&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"381698719384","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 16:44:47', 1, 20.00, 80.00, '2025-07-16 16:43:46', '2025-07-16 16:44:47');
|
||||||
|
INSERT INTO `pay_order` VALUES (45, 'smile04', '9890001', 'MyBatisBook', '380924838419', '2025-07-16 16:54:06', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=SsZLFKzuRwKy%2BfH71Qr%2BhpHFi1zoDdb11O7Exl08xstV628NGoRTBzUeLiZqJWAmlCo6CA4y%2F5SLVdE1shz%2FOPYRJfvRvxIkBbn8qfH79bJyMcd3BIt3ETPPMJSHDZ9mWU1HEAhP4n5rsKBRZHG0mjo%2BxQdoApF6pAwqXBXK6xs9McrV9c%2BC77vMlQ7yMTY6LT9uYmNsFoxekbXYXswMLmiEBX2F%2BNXw77RUKhA0jlMuM1xEydy5LrNaukXgrnS9wLORosTNDRvZnvf4TIk4ln1IYFDYz2WMfLO658bXdvVDW2huxWCqn6Br9VX3I6hj6AXSnSVd7HswstXZACiieQ%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+16%3A54%3A05&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{"out_trade_no":"380924838419","total_amount":100.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 16:55:14', 0, 0.00, 100.00, '2025-07-16 16:54:05', '2025-07-16 16:55:14');
|
||||||
|
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
147
docs/dev-ops/nginx/html/css/index.css
Normal file
147
docs/dev-ops/nginx/html/css/index.css
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/* ========== 全局 ========== */
|
||||||
|
*{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== 拼单列表 ========== */
|
||||||
|
.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;}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
.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;}
|
||||||
|
|
||||||
|
/* ========== 支付弹窗 ========== */
|
||||||
|
.payment-overlay{
|
||||||
|
position:fixed;inset:0;z-index:9999;
|
||||||
|
background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;
|
||||||
|
backdrop-filter:blur(2px);
|
||||||
|
}
|
||||||
|
.payment-modal{
|
||||||
|
width:300px;max-width:90vw;padding:26px 28px 30px;background:#fff;
|
||||||
|
border-radius:12px;box-shadow:0 12px 30px rgba(0,0,0,.18);
|
||||||
|
font-size:14px;line-height:1.45;text-align:center;
|
||||||
|
}
|
||||||
|
.payment-modal h3{font-size:18px;margin:0 0 14px;color:#333;}
|
||||||
|
.payment-modal p{margin:6px 0;color:#555;word-break:break-all;}
|
||||||
|
.payment-modal .copyable{color:#ff5000;cursor:pointer;text-decoration:underline;}
|
||||||
|
|
||||||
|
.modal-buttons{margin-top:22px;display:flex;gap:12px;}
|
||||||
|
.modal-buttons button{
|
||||||
|
flex:1;padding:8px 0;border-radius:6px;font-size:14px;cursor:pointer;border:none;
|
||||||
|
}
|
||||||
|
.confirm-btn{background:#ff5000;color:#fff;}
|
||||||
|
.cancel-btn{background:#f2f3f5;color:#333;}
|
94
docs/dev-ops/nginx/html/css/login.css
Normal file
94
docs/dev-ops/nginx/html/css/login.css
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/* ==================== Reset & 基础 ==================== */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== 登录卡片 ==================== */
|
||||||
|
.login-container {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: center;
|
||||||
|
max-width: 400px;
|
||||||
|
width: 90%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container h1 {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
color: #333;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== Logo ==================== */
|
||||||
|
.logo {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== 二维码 ==================== */
|
||||||
|
.qr-code {
|
||||||
|
margin: 30px 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code img {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: -5px;
|
||||||
|
background: linear-gradient(45deg, #12c2e9, #c471ed, #f64f59);
|
||||||
|
z-index: -1;
|
||||||
|
filter: blur(20px);
|
||||||
|
border-radius: 15px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code:hover::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== 说明文字 ==================== */
|
||||||
|
.instructions {
|
||||||
|
color: #666;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== 动画 ==================== */
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.05); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse {
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
<form name="punchout_form" method="post" action="https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=aeJrx00JaoiPo%2Bd%2BelwPekikVqqcT0UjTQluUAWGtxia0Zp9BuzIljU%2BOrQtBj9vhV9cfh1qFxh4%2BGqpTyJN0ZTTPoQa1U4tT1UE2be5JKcV%2BKFtXfpSL%2BFHx7oPLoqXUnhksF5Q8h82n680LVt%2FFGfwo4mBKuFzJ%2FX1libjCzFwdcIHvosESbClSHb5hmNSyWSmyh4UZra9wvaofUjZvrE2%2FhEYIK01Sl9cTXFvmnCHaGLdKgBPUYdx5zVQryKuV%2BfZgDw9s7kEjPommlx3s0XK0tFb8dmitYc%2FdFvVDjDTJ8YE4PuFDedG2qtmOgJ%2BteXfrUHjWwfsMLTuor9elA%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top¬ify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2×tamp=2025-07-16+13%3A57%3A47&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json">
|
|
||||||
<input type="hidden" name="biz_content" value="{"out_trade_no":"274640446882","total_amount":80.00,"subject":"MyBatisBook","product_code":"FAST_INSTANT_TRADE_PAY"}">
|
|
||||||
<input type="submit" value="立即支付" style="display:none" >
|
|
||||||
</form>
|
|
||||||
<script>document.forms[0].submit();</script>
|
|
BIN
docs/dev-ops/nginx/html/images/goods_info1.png
Normal file
BIN
docs/dev-ops/nginx/html/images/goods_info1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 347 KiB |
BIN
docs/dev-ops/nginx/html/images/goods_info2.png
Normal file
BIN
docs/dev-ops/nginx/html/images/goods_info2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 281 KiB |
BIN
docs/dev-ops/nginx/html/images/goods_info3.png
Normal file
BIN
docs/dev-ops/nginx/html/images/goods_info3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
Before Width: | Height: | Size: 950 KiB |
BIN
docs/dev-ops/nginx/html/images/logo.png
Normal file
BIN
docs/dev-ops/nginx/html/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
BIN
docs/dev-ops/nginx/html/images/qrcode.png
Normal file
BIN
docs/dev-ops/nginx/html/images/qrcode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 455 KiB |
@ -1,123 +1,92 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<title>商品下单支付页</title>
|
<title>手写 MyBatis:渐进式源码实践 - 拼多多</title>
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
text-align: center; /* 添加居中对齐 */
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 50px auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.product-info {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border: 1px solid #ddd; /* 添加边框 */
|
|
||||||
padding: 10px; /* 添加内边距 */
|
|
||||||
text-align: center; /* 添加居中对齐 */
|
|
||||||
}
|
|
||||||
.product-info h2 {
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.product-info p {
|
|
||||||
font-size: 36px; /* 加大字体 */
|
|
||||||
font-weight: bold; /* 加粗字体 */
|
|
||||||
color: red; /* 字体颜色改为红色 */
|
|
||||||
margin: 0; /* 移除默认的margin */
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.order-button {
|
|
||||||
display: block;
|
|
||||||
width: calc(100% - 40px); /* 减去padding的宽度 */
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px auto; /* 添加自动外边距实现居中 */
|
|
||||||
font-size: 16px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #007bff;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 20px; /* 添加圆角 */
|
|
||||||
}
|
|
||||||
.order-button:hover {
|
|
||||||
background-color: #0056b3;
|
|
||||||
}
|
|
||||||
.account-info {
|
|
||||||
font-size: 12px; /* 设置字体大小为9号 */
|
|
||||||
text-align: center; /* 居中对齐 */
|
|
||||||
display: block; /* 使span表现得像块级元素 */
|
|
||||||
margin: 10px 0; /* 添加上下外边距 */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="product-info">
|
|
||||||
<h2>程序员 - 同款机械键盘</h2>
|
|
||||||
|
|
||||||
<img width="350" src="images/keyboard-001.jpg"/>
|
<!-- 现成样式 -->
|
||||||
<p>价格:¥1.68</p>
|
<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>
|
||||||
<button id="orderButton" class="order-button">立即下单「沙箱支付」</button>
|
<div class="swiper-pagination"></div>
|
||||||
<span class="account-info">测试账号:jhmfgu7187@sandbox.com 密码:111111 支付:111111</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<!-- 商品信息 -->
|
||||||
function getCookie(name) {
|
<div class="product-info">
|
||||||
let cookieArr = document.cookie.split(";");
|
<div class="price-row">
|
||||||
for(let i = 0; i < cookieArr.length; i++) {
|
<div class="current-price" id="currentPrice"></div>
|
||||||
let cookiePair = cookieArr[i].split("=");
|
<div class="original-price" id="originalPrice"></div>
|
||||||
if(name == cookiePair[0].trim()) {
|
</div>
|
||||||
return decodeURIComponent(cookiePair[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('orderButton').addEventListener('click', function() {
|
<div class="title" id="goodsTitle">手写 MyBatis:渐进式源码实践(全彩)</div>
|
||||||
var userId = getCookie("loginToken");
|
|
||||||
if (!userId) {
|
|
||||||
window.location.href = "login.html"; // 跳转到登录页
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var productId = "10001";
|
<div class="promo-row">
|
||||||
var url = 'http://127.0.0.1:8092/api/v1/alipay/create_pay_order';
|
<span class="promo-tag">大促优惠</span>
|
||||||
|
<span class="promo-box drop" id="dropPrice"></span>
|
||||||
|
<span class="promo-box sold" id="soldBox"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
var requestBody = {
|
<!-- 拼单区域 -->
|
||||||
userId: userId,
|
<div class="group-buying">
|
||||||
productId: productId
|
<div class="section-title" id="groupTitle"></div>
|
||||||
};
|
|
||||||
|
|
||||||
fetch(url, {
|
<div class="group-users">
|
||||||
method: 'POST',
|
<div class="user-list" id="userList"></div>
|
||||||
headers: {
|
</div>
|
||||||
'Content-Type': 'application/json'
|
</div>
|
||||||
},
|
|
||||||
body: JSON.stringify(requestBody) // 将请求体转换为JSON字符串
|
<!-- 底部操作栏 -->
|
||||||
})
|
<div class="action-bar">
|
||||||
.then(response => response.json()) // 解析JSON格式的响应
|
<div class="action-btn"><i class="fas fa-home"></i><span>首页</span></div>
|
||||||
.then(json => {
|
<div class="action-btn"><i class="fas fa-heart"></i><span>收藏</span></div>
|
||||||
if (json.code === "0000") { // 假设成功的code是"0000"
|
<div class="action-btn"><i class="fas fa-shopping-cart"></i><span>购物车</span></div>
|
||||||
var formHtml = json.data; // 获取data字段中的HTML表单字符串
|
|
||||||
document.body.innerHTML += formHtml; // 将表单添加到页面上
|
<div class="purchase-btn">
|
||||||
document.forms[0].submit(); // 自动提交表单
|
<button class="btn-single" id="btnSingle" data-price="">
|
||||||
} else {
|
<span class="btn-price" id="singlePrice"></span>
|
||||||
console.error('Error:', json.info); // 输出错误信息
|
<span class="btn-label">单独购买</span>
|
||||||
}
|
</button>
|
||||||
})
|
<button class="btn-group" id="btnGroup" data-price="">
|
||||||
.catch(error => console.error('Error:', error));
|
<span class="btn-price" id="groupPrice"></span>
|
||||||
});
|
<span class="btn-label">开团购买</span>
|
||||||
</script>
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付弹窗模板:浏览器不会渲染 -->
|
||||||
|
<template id="tpl-payment">
|
||||||
|
<div class="payment-overlay">
|
||||||
|
<div class="payment-modal">
|
||||||
|
<h3>支付确认</h3>
|
||||||
|
<p>商品金额:<strong id="priceText"></strong></p>
|
||||||
|
<p>买家账号:
|
||||||
|
<span class="copyable" data-copy="kvhmoj3832@sandbox.com">
|
||||||
|
kvhmoj3832@sandbox.com
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>登录密码:111111</p>
|
||||||
|
<p>支付密码:111111</p>
|
||||||
|
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button class="cancel-btn">取消支付</button>
|
||||||
|
<button class="confirm-btn">确认支付</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 逻辑脚本 -->
|
||||||
|
<script src="js/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
347
docs/dev-ops/nginx/html/js/index.js
Normal file
347
docs/dev-ops/nginx/html/js/index.js
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
|
/* ========== 通用工具 ========== */
|
||||||
|
const getCookie = k =>
|
||||||
|
document.cookie
|
||||||
|
.split(';')
|
||||||
|
.map(c => c.trim())
|
||||||
|
.find(c => c.startsWith(k + '='))?.split('=')[1] || null;
|
||||||
|
|
||||||
|
const $ = id => document.getElementById(id);
|
||||||
|
|
||||||
|
/* ----------- 0. DOM 快捷引用 ----------- */
|
||||||
|
const currentPrice = $('currentPrice');
|
||||||
|
const originalPrice = $('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 CFG_API = 'http://127.0.0.1:8091/api/v1/gbm/index/query_group_buy_market_config';
|
||||||
|
|
||||||
|
// 登录检查
|
||||||
|
const loginToken = getCookie('loginToken');
|
||||||
|
if (!loginToken) { location.href = 'login.html'; return; }
|
||||||
|
const username = loginToken;
|
||||||
|
|
||||||
|
// 保存活动 id(开团 / 参团时要用)
|
||||||
|
let activityId = null;
|
||||||
|
|
||||||
|
const POST_BODY = {
|
||||||
|
userId : username,
|
||||||
|
source : 's01',
|
||||||
|
channel: 'c01',
|
||||||
|
goodsId: '9890001'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(CFG_API, {
|
||||||
|
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; }
|
||||||
|
|
||||||
|
activityId = data.activityId;
|
||||||
|
renderGoods(data.goods);
|
||||||
|
renderStatistic(data.teamStatistic);
|
||||||
|
renderTeams(data.teamList, data.goods?.payPrice);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
|
||||||
|
/* ------------- 渲染商品信息 ------------- */
|
||||||
|
function renderGoods(g = {}) {
|
||||||
|
const { originalPrice: op = 0, payPrice = 0, deductionPrice = 0 } = g;
|
||||||
|
currentPrice.textContent = payPrice;
|
||||||
|
originalPrice.textContent = op;
|
||||||
|
dropPrice.textContent = `直降 ¥${deductionPrice}`;
|
||||||
|
|
||||||
|
singlePriceSpan.textContent = `¥${op}`;
|
||||||
|
groupPriceSpan.textContent = `¥${payPrice}`;
|
||||||
|
btnSingle.dataset.price = op;
|
||||||
|
btnGroup.dataset.price = payPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- 渲染统计信息 ------------- */
|
||||||
|
function renderStatistic(stat = {}) {
|
||||||
|
const { allTeamUserCount = 0 } = stat;
|
||||||
|
groupTitle.textContent = `${allTeamUserCount}人在抢,参与可立即拼成`;
|
||||||
|
soldBox.textContent = `${allTeamUserCount}人再抢`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- 渲染拼团列表 ------------- */
|
||||||
|
function renderTeams(list = [], groupPrice = 0) {
|
||||||
|
if (!list?.length) {
|
||||||
|
groupTitle.textContent = '小伙伴,赶紧去开团吧,做村里最靓的仔。';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userList.innerHTML = '';
|
||||||
|
list.forEach(t => userList.appendChild(makeItem(t, groupPrice)));
|
||||||
|
initUserMarquee();
|
||||||
|
initCountdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把 teamId / activityId 写到 user-info 的 dataset 上,便于点击时读取
|
||||||
|
function makeItem(team, price) {
|
||||||
|
const { userId, targetCount, lockCount,
|
||||||
|
validTimeCountdown, teamId, activityId: tActId } = 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"
|
||||||
|
data-teamid="${teamId}"
|
||||||
|
data-activityid="${tActId}">
|
||||||
|
<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) 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 PAY_MALL_URL = 'http://127.0.0.1:8092';
|
||||||
|
const CREATE_PAY_API = `${PAY_MALL_URL}/api/v1/alipay/create_pay_order`;
|
||||||
|
|
||||||
|
// 4.1 支付确认弹窗
|
||||||
|
function showPaymentConfirm(price){
|
||||||
|
if(document.querySelector('.payment-overlay')) return;
|
||||||
|
|
||||||
|
const tpl = document.getElementById('tpl-payment');
|
||||||
|
const overlay = tpl.content.firstElementChild.cloneNode(true);
|
||||||
|
|
||||||
|
overlay.querySelector('#priceText').textContent = `¥${price}`;
|
||||||
|
overlay.querySelector('.copyable').onclick = function(){
|
||||||
|
navigator.clipboard.writeText(this.dataset.copy)
|
||||||
|
.then(()=>alert('买家账号已复制到剪贴板'));
|
||||||
|
};
|
||||||
|
overlay.querySelector('.cancel-btn').onclick = ()=>{
|
||||||
|
document.querySelectorAll('form').forEach(f=>f.remove());
|
||||||
|
overlay.remove();
|
||||||
|
};
|
||||||
|
overlay.querySelector('.confirm-btn').onclick = ()=>{
|
||||||
|
document.querySelector('form')?.submit();
|
||||||
|
overlay.remove();
|
||||||
|
};
|
||||||
|
overlay.addEventListener('click',e=>{
|
||||||
|
if(e.target===overlay) overlay.querySelector('.cancel-btn').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 4.2 单独购买 ---------- */
|
||||||
|
btnSingle.addEventListener('click', () => {
|
||||||
|
if (!getCookie('loginToken')) { location.href = 'login.html'; return; }
|
||||||
|
|
||||||
|
fetch(CREATE_PAY_API, {
|
||||||
|
method : 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body : JSON.stringify({
|
||||||
|
userId : username,
|
||||||
|
productId : POST_BODY.goodsId,
|
||||||
|
marketType: 0
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r=>r.json())
|
||||||
|
.then(json=>{
|
||||||
|
if (json.code!=='0000') return alert(json.info||'下单失败');
|
||||||
|
document.querySelectorAll('form').forEach(f=>f.remove());
|
||||||
|
document.body.insertAdjacentHTML('beforeend', json.data);
|
||||||
|
showPaymentConfirm(btnSingle.dataset.price);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ---------- 4.3 开团购买 ---------- */
|
||||||
|
btnGroup.addEventListener('click', () => {
|
||||||
|
if (!getCookie('loginToken')) { location.href = 'login.html'; return; }
|
||||||
|
|
||||||
|
fetch(CREATE_PAY_API, {
|
||||||
|
method : 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body : JSON.stringify({
|
||||||
|
userId : username,
|
||||||
|
productId : POST_BODY.goodsId,
|
||||||
|
marketType: 1,
|
||||||
|
activityId: activityId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r=>r.json())
|
||||||
|
.then(json=>{
|
||||||
|
if (json.code!=='0000') return alert(json.info||'下单失败');
|
||||||
|
document.querySelectorAll('form').forEach(f=>f.remove());
|
||||||
|
document.body.insertAdjacentHTML('beforeend', json.data);
|
||||||
|
showPaymentConfirm(btnGroup.dataset.price);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ---------- 4.4 参与拼团( buy-btn )---------- */
|
||||||
|
document.body.addEventListener('click', e => {
|
||||||
|
const joinBtn = e.target.closest('.buy-btn');
|
||||||
|
if (!joinBtn) return;
|
||||||
|
|
||||||
|
if (!getCookie('loginToken')) { location.href = 'login.html'; return; }
|
||||||
|
|
||||||
|
// 获取 teamId、activityId
|
||||||
|
const userInfo = joinBtn.closest('.user-item')?.querySelector('.user-info');
|
||||||
|
const teamId = userInfo?.dataset.teamid;
|
||||||
|
const actId = userInfo?.dataset.activityid || activityId;
|
||||||
|
|
||||||
|
if (!teamId) { return alert('拼团信息已失效,请刷新页面'); }
|
||||||
|
|
||||||
|
fetch(CREATE_PAY_API, {
|
||||||
|
method : 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body : JSON.stringify({
|
||||||
|
userId : username,
|
||||||
|
productId : POST_BODY.goodsId,
|
||||||
|
teamId : teamId,
|
||||||
|
activityId: actId,
|
||||||
|
marketType: 1 // 参团
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r=>r.json())
|
||||||
|
.then(json=>{
|
||||||
|
if (json.code!=='0000') return alert(json.info||'参团失败');
|
||||||
|
document.querySelectorAll('form').forEach(f=>f.remove());
|
||||||
|
document.body.insertAdjacentHTML('beforeend', json.data);
|
||||||
|
showPaymentConfirm(joinBtn.dataset.price);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* =====================================================
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
});
|
52
docs/dev-ops/nginx/html/js/login.js
Normal file
52
docs/dev-ops/nginx/html/js/login.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/* -------------------- 配置 -------------------- */
|
||||||
|
const sPayMallUrl = "http://127.0.0.1:8092";
|
||||||
|
|
||||||
|
/* -------------------- 工具函数 -------------------- */
|
||||||
|
function setCookie(name, value, days) {
|
||||||
|
const date = new Date();
|
||||||
|
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
||||||
|
document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- 主逻辑 -------------------- */
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// 1) 先拿二维码 ticket
|
||||||
|
fetch(`${sPayMallUrl}/api/v1/login/weixin_qrcode_ticket`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.code !== "0000") {
|
||||||
|
console.error("获取二维码 ticket 失败:", data.info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ticket = data.data;
|
||||||
|
const qrCodeImg = document.getElementById("qr-code-img");
|
||||||
|
qrCodeImg.src = `https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=${ticket}`;
|
||||||
|
qrCodeImg.classList.remove("pulse");
|
||||||
|
|
||||||
|
// 2) 轮询确认登录
|
||||||
|
const intervalId = setInterval(() => checkLoginStatus(ticket, intervalId), 3000);
|
||||||
|
})
|
||||||
|
.catch(err => console.error("请求失败:", err));
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------------------- 轮询检查登录 -------------------- */
|
||||||
|
function checkLoginStatus(ticket, intervalId) {
|
||||||
|
fetch(`${sPayMallUrl}/api/v1/login/check_login?ticket=${ticket}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.code === "0000") {
|
||||||
|
// 登录成功,停轮询
|
||||||
|
clearInterval(intervalId);
|
||||||
|
|
||||||
|
// 把 token 写入 cookie(30 天)
|
||||||
|
setCookie("loginToken", data.data, 30);
|
||||||
|
|
||||||
|
// 跳转首页
|
||||||
|
window.location.href = "./index.html";
|
||||||
|
} else {
|
||||||
|
console.info("login wait");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => console.error("请求失败:", err));
|
||||||
|
}
|
@ -1,105 +1,30 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<title>S Pay Mall 商城登录页</title>
|
<title>S Pay Mall 商城登录</title>
|
||||||
<style>
|
|
||||||
body {
|
<!-- 独立样式文件 -->
|
||||||
font-family: Arial, sans-serif;
|
<link rel="stylesheet" href="css/login.css" />
|
||||||
background-color: #f5f5f5;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.login-container {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
text-align: center;
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.login-container h1 {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.qr-code {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
.qr-code img {
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
}
|
|
||||||
.instructions {
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<h1>S Pay Mall 商城登录页</h1>
|
<img src="images/logo.png" alt="S Pay Mall Logo" class="logo" />
|
||||||
|
<h1>欢迎登录 - 拼团团</h1>
|
||||||
|
|
||||||
<div class="qr-code">
|
<div class="qr-code">
|
||||||
<img id="qr-code-img" src="images/placeholder.png" alt="微信二维码">
|
<!-- 占位图先显示,加载成功后替换 -->
|
||||||
|
<img id="qr-code-img" src="images/placeholder.png" alt="微信二维码" class="pulse" />
|
||||||
</div>
|
</div>
|
||||||
<p class="instructions">请使用微信扫描二维码登录</p>
|
|
||||||
|
<p class="instructions">
|
||||||
|
请使用微信扫描二维码登录<br />
|
||||||
|
扫码后自动登录商城
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<!-- 独立脚本文件 -->
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
<script src="js/login.js"></script>
|
||||||
// 获取二维码 ticket
|
|
||||||
fetch('http://localhost:8092/api/v1/login/weixin_qrcode_ticket')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.code === "0000") {
|
|
||||||
const ticket = data.data;
|
|
||||||
const qrCodeImg = document.getElementById('qr-code-img');
|
|
||||||
qrCodeImg.src = `https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=${ticket}`;
|
|
||||||
|
|
||||||
// 开始轮询检查登录状态
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
checkLoginStatus(ticket, intervalId);
|
|
||||||
}, 3000); // 每3秒检查一次
|
|
||||||
} else {
|
|
||||||
console.error('获取二维码 ticket 失败:', data.info);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('请求失败:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
function checkLoginStatus(ticket, intervalId) {
|
|
||||||
fetch(`http://localhost:8092/api/v1/login/check_login?ticket=${ticket}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.code === "0000") {
|
|
||||||
console.info("login success");
|
|
||||||
// 停止轮询
|
|
||||||
clearInterval(intervalId);
|
|
||||||
// 保存登录 token 到 cookie,设置有效期为30天
|
|
||||||
setCookie('loginToken', data.data, 30);
|
|
||||||
// 在这里可以重定向到登录后的页面
|
|
||||||
window.location.href = 'index.html'; // 假设登录成功后跳转到首页
|
|
||||||
} else {
|
|
||||||
console.info("login wait");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('请求失败:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCookie(name, value, days) {
|
|
||||||
const date = new Date();
|
|
||||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
||||||
const expires = "expires=" + date.toUTCString();
|
|
||||||
document.cookie = name + "=" + value + ";" + expires + ";path=/";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -27,11 +27,12 @@ public abstract class AbstractOrderService implements IOrderService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public PayOrderEntity createOrder(ShopCartEntity shopCartEntity) throws Exception {
|
public PayOrderEntity createOrder(ShopCartEntity shopCartEntity) throws Exception {
|
||||||
// 1. 查询当前用户是否存在掉单和未支付订单
|
// 1. 查询当前用户是否存在掉单或未支付订单
|
||||||
OrderEntity unpaidOrderEntity = repository.queryUnPayOrder(shopCartEntity);
|
OrderEntity unpaidOrderEntity = repository.queryUnPayOrder(shopCartEntity);
|
||||||
|
|
||||||
// 如果已有未支付订单且状态为支付等待,则直接复用
|
// 如果已有未支付订单且状态为“等待支付”,直接复用该订单
|
||||||
if (unpaidOrderEntity != null && OrderStatusVO.PAY_WAIT.equals(unpaidOrderEntity.getOrderStatusVO())) {
|
if (unpaidOrderEntity != null
|
||||||
|
&& OrderStatusVO.PAY_WAIT.equals(unpaidOrderEntity.getOrderStatusVO())) {
|
||||||
log.info("创建订单-存在,已存在未支付订单。userId:{} productId:{} orderId:{}",
|
log.info("创建订单-存在,已存在未支付订单。userId:{} productId:{} orderId:{}",
|
||||||
shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
|
shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
|
||||||
return PayOrderEntity.builder()
|
return PayOrderEntity.builder()
|
||||||
@ -39,41 +40,64 @@ public abstract class AbstractOrderService implements IOrderService {
|
|||||||
.payUrl(unpaidOrderEntity.getPayUrl())
|
.payUrl(unpaidOrderEntity.getPayUrl())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 如果已有订单仅创建了记录但未生成支付单,则生成支付单
|
// 如果已有订单仅创建了记录但未生成支付单,则进入支付单生成流程
|
||||||
} else if (unpaidOrderEntity != null && OrderStatusVO.CREATE.equals(unpaidOrderEntity.getOrderStatusVO())) {
|
} else if (unpaidOrderEntity != null
|
||||||
|
&& OrderStatusVO.CREATE.equals(unpaidOrderEntity.getOrderStatusVO())) {
|
||||||
log.info("创建订单-存在,存在未创建支付单订单,创建支付单开始 userId:{} productId:{} orderId:{}",
|
log.info("创建订单-存在,存在未创建支付单订单,创建支付单开始 userId:{} productId:{} orderId:{}",
|
||||||
shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
|
shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
|
||||||
|
|
||||||
Integer marketType = unpaidOrderEntity.getMarketType();
|
Integer marketType = unpaidOrderEntity.getMarketType();
|
||||||
BigDecimal marketDeductionAmount = unpaidOrderEntity.getMarketDeductionAmount();
|
BigDecimal marketDeductionAmount = unpaidOrderEntity.getMarketDeductionAmount();
|
||||||
PayOrderEntity payOrderEntity;
|
PayOrderEntity payOrderEntity;
|
||||||
|
|
||||||
if (MarketTypeVO.GROUP_BUY_MARKET.getCode().equals(marketType) && marketDeductionAmount == null) {
|
// === marketType 分支处理 ===
|
||||||
|
if (MarketTypeVO.GROUP_BUY_MARKET.getCode().equals(marketType)
|
||||||
|
&& marketDeductionAmount == null) {
|
||||||
|
// 1) 是拼团类型,且还未计算优惠(deductionAmount == null)时
|
||||||
|
// 先锁定优惠(如拼团满减),获取 discount 对象
|
||||||
MarketPayDiscountEntity discount = lockMarketPayOrder(
|
MarketPayDiscountEntity discount = lockMarketPayOrder(
|
||||||
shopCartEntity.getUserId(), shopCartEntity.getTeamId(),
|
shopCartEntity.getUserId(),
|
||||||
shopCartEntity.getActivityId(), shopCartEntity.getProductId(),
|
shopCartEntity.getTeamId(),
|
||||||
|
shopCartEntity.getActivityId(),
|
||||||
|
shopCartEntity.getProductId(),
|
||||||
unpaidOrderEntity.getOrderId());
|
unpaidOrderEntity.getOrderId());
|
||||||
|
// 2) 再发起预支付,传入优惠后金额
|
||||||
payOrderEntity = doPrepayOrder(
|
payOrderEntity = doPrepayOrder(
|
||||||
shopCartEntity.getUserId(), shopCartEntity.getProductId(),
|
shopCartEntity.getUserId(),
|
||||||
unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(),
|
shopCartEntity.getProductId(),
|
||||||
unpaidOrderEntity.getTotalAmount(), discount);
|
unpaidOrderEntity.getProductName(),
|
||||||
|
unpaidOrderEntity.getOrderId(),
|
||||||
|
unpaidOrderEntity.getTotalAmount(),
|
||||||
|
discount);
|
||||||
|
|
||||||
} else if (MarketTypeVO.GROUP_BUY_MARKET.getCode().equals(marketType)) {
|
} else if (MarketTypeVO.GROUP_BUY_MARKET.getCode().equals(marketType)) {
|
||||||
|
// 是拼团类型,但已经计算过优惠,直接使用 payAmount(已扣优惠)
|
||||||
payOrderEntity = doPrepayOrder(
|
payOrderEntity = doPrepayOrder(
|
||||||
shopCartEntity.getUserId(), shopCartEntity.getProductId(),
|
shopCartEntity.getUserId(),
|
||||||
unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(),
|
shopCartEntity.getProductId(),
|
||||||
|
unpaidOrderEntity.getProductName(),
|
||||||
|
unpaidOrderEntity.getOrderId(),
|
||||||
unpaidOrderEntity.getPayAmount());
|
unpaidOrderEntity.getPayAmount());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// 其他类型(如单独购买或普通下单),按总价全额下单
|
||||||
payOrderEntity = doPrepayOrder(
|
payOrderEntity = doPrepayOrder(
|
||||||
shopCartEntity.getUserId(), shopCartEntity.getProductId(),
|
shopCartEntity.getUserId(),
|
||||||
unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(),
|
shopCartEntity.getProductId(),
|
||||||
|
unpaidOrderEntity.getProductName(),
|
||||||
|
unpaidOrderEntity.getOrderId(),
|
||||||
unpaidOrderEntity.getTotalAmount());
|
unpaidOrderEntity.getTotalAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回已生成支付单的订单信息
|
||||||
return PayOrderEntity.builder()
|
return PayOrderEntity.builder()
|
||||||
.orderId(payOrderEntity.getOrderId())
|
.orderId(payOrderEntity.getOrderId())
|
||||||
.payUrl(payOrderEntity.getPayUrl())
|
.payUrl(payOrderEntity.getPayUrl())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ———— 以下为“全新下单”流程 ————
|
||||||
|
|
||||||
// 2. 查询商品信息
|
// 2. 查询商品信息
|
||||||
ProductEntity productEntity = port.queryProductByProductId(shopCartEntity.getProductId());
|
ProductEntity productEntity = port.queryProductByProductId(shopCartEntity.getProductId());
|
||||||
|
|
||||||
@ -89,16 +113,18 @@ public abstract class AbstractOrderService implements IOrderService {
|
|||||||
.build();
|
.build();
|
||||||
this.doSaveOrder(orderAggregate);
|
this.doSaveOrder(orderAggregate);
|
||||||
|
|
||||||
// 4. 如果是拼团,发起营销锁单
|
// 4. 如果是拼团(marketType == GROUP_BUY_MARKET),先锁定营销优惠
|
||||||
MarketPayDiscountEntity marketPayDiscountEntity = null;
|
MarketPayDiscountEntity marketPayDiscountEntity = null;
|
||||||
if (MarketTypeVO.GROUP_BUY_MARKET.equals(shopCartEntity.getMarketTypeVO())) {
|
if (MarketTypeVO.GROUP_BUY_MARKET.equals(shopCartEntity.getMarketTypeVO())) {
|
||||||
marketPayDiscountEntity = this.lockMarketPayOrder(
|
marketPayDiscountEntity = this.lockMarketPayOrder( //调用拼团交易系统的锁单逻辑
|
||||||
shopCartEntity.getUserId(), shopCartEntity.getTeamId(),
|
shopCartEntity.getUserId(),
|
||||||
shopCartEntity.getActivityId(), shopCartEntity.getProductId(),
|
shopCartEntity.getTeamId(),
|
||||||
|
shopCartEntity.getActivityId(),
|
||||||
|
shopCartEntity.getProductId(),
|
||||||
orderEntity.getOrderId());
|
orderEntity.getOrderId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 创建支付订单
|
// 5. 创建支付订单(预支付),如果有拼团优惠则传入 discount,否则按原价下单
|
||||||
PayOrderEntity payOrderEntity = doPrepayOrder(
|
PayOrderEntity payOrderEntity = doPrepayOrder(
|
||||||
shopCartEntity.getUserId(),
|
shopCartEntity.getUserId(),
|
||||||
productEntity.getProductId(),
|
productEntity.getProductId(),
|
||||||
@ -110,12 +136,14 @@ public abstract class AbstractOrderService implements IOrderService {
|
|||||||
log.info("创建订单-完成,生成支付单。userId: {} orderId: {} payUrl: {}",
|
log.info("创建订单-完成,生成支付单。userId: {} orderId: {} payUrl: {}",
|
||||||
shopCartEntity.getUserId(), orderEntity.getOrderId(), payOrderEntity.getPayUrl());
|
shopCartEntity.getUserId(), orderEntity.getOrderId(), payOrderEntity.getPayUrl());
|
||||||
|
|
||||||
|
// 返回新订单的支付链接
|
||||||
return PayOrderEntity.builder()
|
return PayOrderEntity.builder()
|
||||||
.orderId(orderEntity.getOrderId())
|
.orderId(orderEntity.getOrderId())
|
||||||
.payUrl(payOrderEntity.getPayUrl())
|
.payUrl(payOrderEntity.getPayUrl())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存订单
|
* 保存订单
|
||||||
*/
|
*/
|
||||||
|
@ -56,7 +56,7 @@ public class OrderService extends AbstractOrderService{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount, MarketPayDiscountEntity marketPayDiscountEntity) throws AlipayApiException {
|
protected PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount, MarketPayDiscountEntity marketPayDiscountEntity) throws AlipayApiException {
|
||||||
// 支付金额
|
// 支付金额,如果走拼团流程就取PayPrice,否则就是totalAmount
|
||||||
BigDecimal payAmount = null == marketPayDiscountEntity ? totalAmount : marketPayDiscountEntity.getPayPrice();
|
BigDecimal payAmount = null == marketPayDiscountEntity ? totalAmount : marketPayDiscountEntity.getPayPrice();
|
||||||
|
|
||||||
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
|
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
|
||||||
|
@ -54,6 +54,7 @@ public class AliPayController implements IPayService {
|
|||||||
.productId(productId)
|
.productId(productId)
|
||||||
.teamId(teamId)
|
.teamId(teamId)
|
||||||
.marketTypeVO(MarketTypeVO.valueOf(marketType))
|
.marketTypeVO(MarketTypeVO.valueOf(marketType))
|
||||||
|
.activityId(createPayRequestDTO.getActivityId())
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
log.info("商品下单,根据商品ID创建支付单完成 userId:{} productId:{} orderId:{}", userId, productId, payOrderEntity.getOrderId());
|
log.info("商品下单,根据商品ID创建支付单完成 userId:{} productId:{} orderId:{}", userId, productId, payOrderEntity.getOrderId());
|
||||||
|
@ -25,12 +25,13 @@ public class NoPayNotifyOrderJob {
|
|||||||
private final AlipayClient alipayClient;
|
private final AlipayClient alipayClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每 3 秒执行一次,扫描超过1分钟未收到回调的待支付订单
|
* 每 10 秒执行一次,扫描超过1分钟未收到回调的待支付订单
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0/3 * * * * ?")
|
@Scheduled(cron = "0/10 * * * * ?")
|
||||||
public void exec() {
|
public void exec() {
|
||||||
try {
|
try {
|
||||||
log.info("任务:检测未接收到或未正确处理的支付回调通知");
|
log.info("任务:检测未接收到或未正确处理的支付回调通知");
|
||||||
|
//查找所有STATUS为PAY_WAIT的订单
|
||||||
List<String> orderIds = orderService.queryNoPayNotifyOrder();
|
List<String> orderIds = orderService.queryNoPayNotifyOrder();
|
||||||
if (null == orderIds || orderIds.isEmpty()) return;
|
if (null == orderIds || orderIds.isEmpty()) return;
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ public class TimeoutCloseOrderJob {
|
|||||||
private IOrderService orderService;
|
private IOrderService orderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每10分钟执行一次,扫描超时未支付订单
|
* 每 15 分钟执行一次,扫描超时未支付订单
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0 0/30 * * * ?")
|
@Scheduled(cron = "0 0/15 * * * ?")
|
||||||
public void exec() {
|
public void exec() {
|
||||||
try {
|
try {
|
||||||
log.info("任务;超时30分钟订单关闭");
|
log.info("任务;超时30分钟订单关闭");
|
||||||
@ -31,11 +31,12 @@ public class TimeoutCloseOrderJob {
|
|||||||
}
|
}
|
||||||
// 遍历订单,逐一关闭并记录结果
|
// 遍历订单,逐一关闭并记录结果
|
||||||
for (String orderId : orderIds) {
|
for (String orderId : orderIds) {
|
||||||
|
// 设置 STATUS为CLOSE
|
||||||
boolean status = orderService.changeOrderClose(orderId);
|
boolean status = orderService.changeOrderClose(orderId);
|
||||||
log.info("定时任务,超时30分钟订单关闭 orderId: {} status:{}", orderId, status);
|
log.info("定时任务,超时15分钟订单关闭 orderId: {} status:{}", orderId, status);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("定时任务,超时30分钟订单关闭失败", e);
|
log.error("定时任务,超时15分钟订单关闭失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,9 @@ public class MessageTextEntity {
|
|||||||
@XStreamAlias("MsgId")
|
@XStreamAlias("MsgId")
|
||||||
private String msgId;
|
private String msgId;
|
||||||
|
|
||||||
|
@XStreamAlias("MsgID")
|
||||||
|
private String msgID;
|
||||||
|
|
||||||
@XStreamAlias("Status")
|
@XStreamAlias("Status")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
@ -84,6 +87,14 @@ public class MessageTextEntity {
|
|||||||
this.msgId = msgId;
|
this.msgId = msgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMsgID() {
|
||||||
|
return msgID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsgID(String msgID) {
|
||||||
|
this.msgID = msgID;
|
||||||
|
}
|
||||||
|
|
||||||
public String getStatus() {
|
public String getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user