From 4d6b3dfe2f0f370779d970ef6303551d3edf69ef Mon Sep 17 00:00:00 2001 From: cst61 Date: Tue, 28 Jul 2026 11:56:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E6=83=A0=E5=8D=B7=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../rj/controller/LbUserCouponController.java | 17 ++ .../com/rj/service/ILbUserCouponService.java | 10 + .../rj/service/impl/LbGoodsServiceImpl.java | 2 +- .../service/impl/LbUserCouponServiceImpl.java | 161 +++++++++- src/main/sql/consignment_goods.sql | 18 ++ src/main/sql/lb_user.sql | 80 ++--- src/main/sql/member_info.sql | 47 +++ src/main/sql/数据切割/过程和步骤.sql | 68 +++++ src/test/java/com/cst/video/TestVideo1.java | 282 ++++++++++++++++++ 10 files changed, 638 insertions(+), 49 deletions(-) create mode 100644 src/main/sql/consignment_goods.sql create mode 100644 src/main/sql/member_info.sql create mode 100644 src/main/sql/数据切割/过程和步骤.sql create mode 100644 src/test/java/com/cst/video/TestVideo1.java diff --git a/pom.xml b/pom.xml index d7a094d..074cfa6 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.cst AIDriverEEBackend - 1.260504.1-SNAPSHOT + 1.260711.1-SNAPSHOT Langchain4j-rj Langchain4j-rj20250803 diff --git a/src/main/java/com/rj/controller/LbUserCouponController.java b/src/main/java/com/rj/controller/LbUserCouponController.java index 033b5eb..06f8945 100644 --- a/src/main/java/com/rj/controller/LbUserCouponController.java +++ b/src/main/java/com/rj/controller/LbUserCouponController.java @@ -121,6 +121,23 @@ public class LbUserCouponController { return ResponseEntity.badRequest().body(result); } + @GetMapping("/calculateMonthlyAmount") + @Operation(summary = "计算用户本月从开始日期到月末的优惠券金额") + public ResponseEntity> calculateMonthlyAmount( + @Parameter(description = "用户ID", required = true) + @RequestParam String userId, + @Parameter(description = "用户昵称") + @RequestParam(required = false) String userNickname, + @Parameter(description = "开始日期,格式 yyyy-MM-dd") + @RequestParam(required = false) String startDate) { + Map result = lbUserCouponService.calculateUserMonthlyAmount(userId, userNickname, startDate); + Boolean success = (Boolean) result.get("success"); + if (success != null && success) { + return ResponseEntity.ok(result); + } + return ResponseEntity.badRequest().body(result); + } + @GetMapping("/export") @Operation(summary = "导出用户优惠券Excel", description = "按照分页查询的相同条件导出用户优惠券数据") public void export( diff --git a/src/main/java/com/rj/service/ILbUserCouponService.java b/src/main/java/com/rj/service/ILbUserCouponService.java index c71196a..e12a0fe 100644 --- a/src/main/java/com/rj/service/ILbUserCouponService.java +++ b/src/main/java/com/rj/service/ILbUserCouponService.java @@ -47,6 +47,16 @@ public interface ILbUserCouponService extends IService { */ Map monthlyStatistic(String tenantId, String couponDateStart, String couponDateEnd); + /** + * 计算用户本月从开始日期到月末的优惠券金额 + * + * @param userId 用户ID + * @param userNickname 用户昵称 + * @param startDate 开始日期,格式 yyyy-MM-dd + * @return 计算结果 + */ + Map calculateUserMonthlyAmount(String userId, String userNickname, String startDate); + /** * 导出用户优惠券Excel * diff --git a/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java b/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java index 102a541..d7343ed 100644 --- a/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java +++ b/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java @@ -668,7 +668,7 @@ public class LbGoodsServiceImpl extends ServiceImpl impl if (buyResult.success()) { successCount++; successTotalAmount += goods.getTotalMoney().intValue(); - Thread.sleep(1000); + Thread.sleep(3000); } else { failCount++; if (buyResult.orderAlreadyGrabbed()) { diff --git a/src/main/java/com/rj/service/impl/LbUserCouponServiceImpl.java b/src/main/java/com/rj/service/impl/LbUserCouponServiceImpl.java index ef685c7..573ad56 100644 --- a/src/main/java/com/rj/service/impl/LbUserCouponServiceImpl.java +++ b/src/main/java/com/rj/service/impl/LbUserCouponServiceImpl.java @@ -32,12 +32,14 @@ import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.time.Duration; +import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -444,6 +446,72 @@ public class LbUserCouponServiceImpl } } + private List fillMissingWeekdayRecords(List records, LocalDate start, LocalDate end) { + if (records == null || records.isEmpty()) { + return records; + } + + Map userInfoMap = new LinkedHashMap<>(); + Map> userDateMap = new HashMap<>(); + + for (LbUserCoupon record : records) { + String userId = record.getUserId(); + if (userId == null) { + continue; + } + + if (!userInfoMap.containsKey(userId)) { + userInfoMap.put(userId, record); + } + + userDateMap.computeIfAbsent(userId, k -> new LinkedHashSet<>()); + if (record.getCouponDate() != null) { + userDateMap.get(userId).add(record.getCouponDate()); + } + } + + List result = new ArrayList<>(records); + LocalDateTime now = LocalDateTime.now(); + + for (Map.Entry entry : userInfoMap.entrySet()) { + String userId = entry.getKey(); + LbUserCoupon template = entry.getValue(); + Set existingDates = userDateMap.getOrDefault(userId, new LinkedHashSet<>()); + + LocalDate current = start; + while (!current.isAfter(end)) { + DayOfWeek dayOfWeek = current.getDayOfWeek(); + if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) { + if (!existingDates.contains(current)) { + LbUserCoupon missingRecord = new LbUserCoupon(); + missingRecord.setId(UUID.randomUUID().toString()); + missingRecord.setTenantId(template.getTenantId()); + missingRecord.setUserId(userId); + missingRecord.setUserPhone(template.getUserPhone()); + missingRecord.setUserNickname(template.getUserNickname()); + missingRecord.setParentPhone(template.getParentPhone()); + missingRecord.setParentNickname(template.getParentNickname()); + missingRecord.setCouponAmount(BigDecimal.ZERO); + missingRecord.setCouponDate(current); + missingRecord.setDataType(template.getDataType()); + missingRecord.setCreateTime(now); + missingRecord.setUpdateTime(now); + result.add(missingRecord); + } + } + current = current.plusDays(1); + } + } + + result.sort((a, b) -> { + BigDecimal amountA = a.getCouponAmount() != null ? a.getCouponAmount() : BigDecimal.ZERO; + BigDecimal amountB = b.getCouponAmount() != null ? b.getCouponAmount() : BigDecimal.ZERO; + return amountB.compareTo(amountA); + }); + + return result; + } + @Override public Map add(LbUserCoupon entity) { Map result = new HashMap<>(); @@ -627,14 +695,34 @@ public class LbUserCouponServiceImpl queryWrapper.orderByDesc(LbUserCoupon::getCouponAmount); - Page page = this.page(new Page<>(current, size), queryWrapper); - result.put("success", true); - result.put("message", "查询成功"); - result.put("data", page.getRecords()); - result.put("total", page.getTotal()); - result.put("current", page.getCurrent()); - result.put("size", page.getSize()); - result.put("pages", page.getPages()); + if (start != null && end != null) { + List allRecords = this.list(queryWrapper); + List filledRecords = fillMissingWeekdayRecords(allRecords, start, end); + + int total = filledRecords.size(); + int pages = (total + size - 1) / size; + int fromIndex = (current - 1) * size; + int toIndex = Math.min(fromIndex + size, total); + + List pageRecords = fromIndex < total ? filledRecords.subList(fromIndex, toIndex) : new ArrayList<>(); + + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", pageRecords); + result.put("total", (long) total); + result.put("current", (long) current); + result.put("size", (long) size); + result.put("pages", (long) pages); + } else { + Page page = this.page(new Page<>(current, size), queryWrapper); + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", page.getRecords()); + result.put("total", page.getTotal()); + result.put("current", page.getCurrent()); + result.put("size", page.getSize()); + result.put("pages", page.getPages()); + } return result; } catch (Exception e) { log.error("用户优惠券分页查询异常", e); @@ -726,6 +814,63 @@ public class LbUserCouponServiceImpl } } + @Override + public Map calculateUserMonthlyAmount(String userId, String userNickname, String startDate) { + Map result = new HashMap<>(); + try { + if (userId == null || userId.trim().isEmpty()) { + result.put("success", false); + result.put("message", "userId不能为空"); + return result; + } + + LocalDate start = parseDate(startDate); + if (startDate != null && !startDate.trim().isEmpty() && start == null) { + result.put("success", false); + result.put("message", "startDate 格式错误,请使用 yyyy-MM-dd"); + return result; + } + if (start == null) { + start = LocalDate.now(); + } + + LocalDate end = start.withDayOfMonth(start.lengthOfMonth()); + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(LbUserCoupon::getUserId, userId.trim()); + if (userNickname != null && !userNickname.trim().isEmpty()) { + queryWrapper.like(LbUserCoupon::getUserNickname, userNickname.trim()); + } + queryWrapper.eq(LbUserCoupon::getDataType, "data_detail"); + queryWrapper.ge(LbUserCoupon::getCouponDate, start); + queryWrapper.le(LbUserCoupon::getCouponDate, end); + + List records = this.list(queryWrapper); + BigDecimal totalAmount = records.stream() + .map(r -> r.getCouponAmount() != null ? r.getCouponAmount() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + List filledRecords = fillMissingWeekdayRecords(records, start, end); + + result.put("success", true); + result.put("message", "计算成功"); + result.put("userId", userId); + result.put("userNickname", userNickname); + result.put("startDate", start.format(DATE_FORMATTER)); + result.put("endDate", end.format(DATE_FORMATTER)); + result.put("totalAmount", totalAmount); + result.put("recordCount", records.size()); + result.put("dailyDetail", filledRecords); + return result; + + } catch (Exception e) { + log.error("计算用户本月优惠券金额异常", e); + result.put("success", false); + result.put("message", "计算异常:" + e.getMessage()); + return result; + } + } + @Override public void exportExcel(String tenantId, String userId, diff --git a/src/main/sql/consignment_goods.sql b/src/main/sql/consignment_goods.sql new file mode 100644 index 0000000..d6754dd --- /dev/null +++ b/src/main/sql/consignment_goods.sql @@ -0,0 +1,18 @@ +-- business_shop.consignment_goods definition + +CREATE TABLE `consignment_goods` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `seller` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '卖家', + `title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题', + `image` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '商品图', + `selling_price` decimal(10,2) NOT NULL COMMENT '售价', + `display_status` int DEFAULT '1' COMMENT '是否显示(1显示,0隐藏)', + `status` int DEFAULT '0' COMMENT '状态(1已售,0未售)', + `create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人', + `update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `quantity` int DEFAULT '0' COMMENT '商品数量', + `unit` varchar(50) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '商品单位', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='寄售商品表'; diff --git a/src/main/sql/lb_user.sql b/src/main/sql/lb_user.sql index c7db9b4..9cc003f 100644 --- a/src/main/sql/lb_user.sql +++ b/src/main/sql/lb_user.sql @@ -1,46 +1,48 @@ -SET NAMES utf8mb4; - CREATE TABLE `lb_user` ( - `id` BIGINT NOT NULL COMMENT '用户ID(外部系统主键)', - `pid` BIGINT DEFAULT NULL COMMENT '上级用户ID', - `username` VARCHAR(64) DEFAULT NULL COMMENT '用户名(常为手机号)', - `nickname` VARCHAR(100) DEFAULT NULL COMMENT '昵称', - `mobile` VARCHAR(32) DEFAULT NULL COMMENT '手机号', - `password` VARCHAR(64) DEFAULT NULL COMMENT '密码(加密后)', - `salt` VARCHAR(32) DEFAULT NULL COMMENT '密码盐', - `sex` VARCHAR(8) DEFAULT NULL COMMENT '性别', - `avatar` VARCHAR(512) DEFAULT NULL COMMENT '头像路径', - `invite` VARCHAR(32) DEFAULT NULL COMMENT '邀请码', - `level` INT NOT NULL DEFAULT 0 COMMENT '等级', - `birthday` DATE DEFAULT NULL COMMENT '生日', - `money` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '余额', - `coupon` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '优惠券金额', - `self_bonus` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '自购奖金', - `share_bonus` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '分享奖金', - `score` INT NOT NULL DEFAULT 0 COMMENT '积分', - `last_time` DATETIME DEFAULT NULL COMMENT '最后登录时间', - `last_ip` VARCHAR(45) DEFAULT NULL COMMENT '最后登录IP', - `join_time` DATETIME DEFAULT NULL COMMENT '注册时间', - `join_ip` VARCHAR(45) DEFAULT NULL COMMENT '注册IP', - `token` VARCHAR(255) DEFAULT NULL COMMENT '登录令牌', - `created_at` DATETIME DEFAULT NULL COMMENT '创建时间', - `updated_at` DATETIME DEFAULT NULL COMMENT '更新时间', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态', - `viptime` DATETIME DEFAULT NULL COMMENT 'VIP到期时间', - `is_vip` TINYINT NOT NULL DEFAULT 0 COMMENT '是否VIP:0否 1是', - `contract` VARCHAR(512) DEFAULT NULL COMMENT '签约合同文件路径', - `max_order` INT NOT NULL DEFAULT 0 COMMENT '最大订单数', - `is_resell` TINYINT NOT NULL DEFAULT 0 COMMENT '是否可转卖:0否 1是', - `yesterday_sell_count` INT NOT NULL DEFAULT 0 COMMENT '昨日卖出笔数', - `today_buy_count` INT NOT NULL DEFAULT 0 COMMENT '今日买入笔数', - `today_buy_total` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '今日买入总额', - `today_sell_total` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '今日卖出总额', - `poor` TINYINT NOT NULL DEFAULT 0 COMMENT '贫困标识/标记', - `pname` VARCHAR(100) DEFAULT NULL COMMENT '上级用户昵称(接口关联字段,可选)', + `id` bigint NOT NULL COMMENT '用户ID(外部系统主键)', + `pid` bigint DEFAULT NULL COMMENT '上级用户ID', + `tenant_id` varchar(64) DEFAULT NULL COMMENT '租户id', + `username` varchar(64) DEFAULT NULL COMMENT '用户名(常为手机号)', + `nickname` varchar(100) DEFAULT NULL COMMENT '昵称', + `mobile` varchar(32) DEFAULT NULL COMMENT '手机号', + `password` varchar(64) DEFAULT NULL COMMENT '密码(加密后)', + `salt` varchar(32) DEFAULT NULL COMMENT '密码盐', + `sex` varchar(8) DEFAULT NULL COMMENT '性别', + `avatar` varchar(512) DEFAULT NULL COMMENT '头像路径', + `invite` varchar(32) DEFAULT NULL COMMENT '邀请码', + `level` int NOT NULL DEFAULT '0' COMMENT '等级', + `birthday` date DEFAULT NULL COMMENT '生日', + `money` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '余额', + `coupon` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '优惠券金额', + `self_bonus` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '自购奖金', + `share_bonus` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '分享奖金', + `score` int NOT NULL DEFAULT '0' COMMENT '积分', + `last_time` datetime DEFAULT NULL COMMENT '最后登录时间', + `last_ip` varchar(45) DEFAULT NULL COMMENT '最后登录IP', + `join_time` datetime DEFAULT NULL COMMENT '注册时间', + `join_ip` varchar(45) DEFAULT NULL COMMENT '注册IP', + `token` varchar(255) DEFAULT NULL COMMENT '登录令牌', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '更新时间', + `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态', + `viptime` datetime DEFAULT NULL COMMENT 'VIP到期时间', + `is_vip` tinyint NOT NULL DEFAULT '0' COMMENT '是否VIP:0否 1是', + `contract` varchar(512) DEFAULT NULL COMMENT '签约合同文件路径', + `max_order` int NOT NULL DEFAULT '0' COMMENT '最大订单数', + `is_resell` tinyint NOT NULL DEFAULT '0' COMMENT '是否可转卖:0否 1是', + `yesterday_sell_count` int NOT NULL DEFAULT '0' COMMENT '昨日卖出笔数', + `today_buy_count` int NOT NULL DEFAULT '0' COMMENT '今日买入笔数', + `today_buy_total` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '今日买入总额', + `today_sell_total` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '今日卖出总额', + `poor` tinyint NOT NULL DEFAULT '0' COMMENT '贫困标识/标记', + `pname` varchar(100) DEFAULT NULL COMMENT '上级用户昵称(接口关联字段,可选)', + `pmobile` varchar(32) DEFAULT NULL COMMENT '上级用户的电话', PRIMARY KEY (`id`), KEY `idx_pid` (`pid`), KEY `idx_mobile` (`mobile`), KEY `idx_username` (`username`), KEY `idx_join_time` (`join_time`), KEY `idx_updated_at` (`updated_at`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='LB 用户表(hxrd 后台 user/select 同步)'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='LB 用户表( 后台 user/select 同步)'; + + diff --git a/src/main/sql/member_info.sql b/src/main/sql/member_info.sql new file mode 100644 index 0000000..a8500f6 --- /dev/null +++ b/src/main/sql/member_info.sql @@ -0,0 +1,47 @@ +-- business_shop.member_info definition + +CREATE TABLE `member_info` ( + `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键ID', + `phone` varchar(20) DEFAULT NULL COMMENT '手机号/账号', + `nickname` varchar(100) DEFAULT NULL COMMENT '昵称', + `avatar` varchar(200) DEFAULT NULL COMMENT '头像', + `password` varchar(255) DEFAULT NULL COMMENT '密码', + `invite_code` varchar(50) DEFAULT NULL COMMENT '邀请码', + `parent_id` varchar(50) DEFAULT '' COMMENT '上级ID', + `parent_code` varchar(50) DEFAULT NULL COMMENT '上级邀请码', + `user_level` varchar(50) DEFAULT 'normal' COMMENT '用户等级(normal-普通用户,referral-推荐人,shopkeeper-店长,second_shopkeeper-二代店长)', + `available_order_count` int DEFAULT '0' COMMENT '当天可抢订单数量', + `vip_end_time` datetime DEFAULT NULL COMMENT '新人体验VIP截止时间', + `is_priority_buy` int DEFAULT '0' COMMENT '是否优先抢购(0-禁用,1-启用)', + `can_consign` int DEFAULT '1' COMMENT '是否可以寄卖(0-禁用,1-启用)', + `status` int DEFAULT '1' COMMENT '状态(0-禁用,1-正常)', + `balance` decimal(18,3) DEFAULT '0.000' COMMENT '余额', + `coupon_balance` decimal(18,3) DEFAULT '0.000' COMMENT '优惠券', + `personal_bonus` decimal(18,3) DEFAULT '0.000' COMMENT '个人奖金', + `promotion_bonus` decimal(18,3) DEFAULT '0.000' COMMENT '推广奖金', + `contract_status` varchar(50) DEFAULT NULL COMMENT '合同状态', + `today_buy_amount` decimal(18,3) DEFAULT '0.000' COMMENT '今日购买总金额', + `today_sell_amount` decimal(18,3) DEFAULT '0.000' COMMENT '今日卖出总金额', + `yesterday_new_count` int DEFAULT '0' COMMENT '昨日卖出数量', + `today_buy_count` int DEFAULT '0' COMMENT '今日购买数量', + `available_goods_count` int DEFAULT '0' COMMENT '商品可抢单数', + `bank_card_number` varchar(50) DEFAULT NULL COMMENT '银行卡号', + `bank_name` varchar(100) DEFAULT NULL COMMENT '开户行', + `bank_card_name` varchar(50) DEFAULT NULL COMMENT '银行卡姓名', + `bank_phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '银行卡手机号', + `alipay_account` varchar(100) DEFAULT NULL COMMENT '支付宝账号', + `alipay_name` varchar(50) DEFAULT NULL COMMENT '支付宝姓名', + `alipay_qr_code` varchar(255) DEFAULT NULL COMMENT '支付宝二维码', + `alipay_phone` varchar(20) DEFAULT NULL COMMENT '支付宝手机号', + `create_by` varchar(255) DEFAULT NULL COMMENT '创建人', + `update_by` varchar(255) DEFAULT NULL COMMENT '更新人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `audit_status` int DEFAULT '0' COMMENT '审核状态(0-未审核,1-已审核)', + PRIMARY KEY (`id`), + KEY `idx_phone` (`phone`), + KEY `idx_parent_id` (`parent_id`), + KEY `idx_user_level` (`user_level`), + KEY `idx_status` (`status`), + KEY `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会员表'; diff --git a/src/main/sql/数据切割/过程和步骤.sql b/src/main/sql/数据切割/过程和步骤.sql new file mode 100644 index 0000000..49f68d5 --- /dev/null +++ b/src/main/sql/数据切割/过程和步骤.sql @@ -0,0 +1,68 @@ + + +INSERT INTO member_info ( + id, + phone, + nickname, + avatar, + password, + invite_code, + parent_id, + user_level, + available_order_count, + vip_end_time, + is_priority_buy, + can_consign, + status, + balance, + coupon_balance, + personal_bonus, + promotion_bonus, + contract_status, + today_buy_amount, + today_sell_amount, + yesterday_new_count, + today_buy_count, + create_time, + update_time, + audit_status +) +SELECT + CAST(u.id AS CHAR(50)) AS id, + COALESCE(u.mobile, u.username) AS phone, + u.nickname, + u.avatar, + u.password, + u.invite AS invite_code, + CAST(u.pid AS CHAR(50)) AS parent_id, + CASE + WHEN u.level = 0 THEN 'normal' + WHEN u.level = 1 THEN 'referral' + WHEN u.level = 2 THEN 'shopkeeper' + WHEN u.level >= 3 THEN 'second_shopkeeper' + ELSE 'normal' + END AS user_level, + u.max_order AS available_order_count, + u.viptime AS vip_end_time, + u.is_vip AS is_priority_buy, + u.is_resell AS can_consign, + u.status, + u.money AS balance, + u.coupon AS coupon_balance, + u.self_bonus AS personal_bonus, + u.share_bonus AS promotion_bonus, + CASE WHEN u.contract IS NOT NULL AND u.contract != '' THEN 'signed' ELSE NULL END AS contract_status, + u.today_buy_total AS today_buy_amount, + u.today_sell_total AS today_sell_amount, + u.yesterday_sell_count AS yesterday_new_count, + u.today_buy_count, + u.created_at AS create_time, + u.updated_at AS update_time, + 0 AS audit_status +FROM lb_user u; + + + + + + diff --git a/src/test/java/com/cst/video/TestVideo1.java b/src/test/java/com/cst/video/TestVideo1.java new file mode 100644 index 0000000..464407e --- /dev/null +++ b/src/test/java/com/cst/video/TestVideo1.java @@ -0,0 +1,282 @@ +package com.cst.video; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.*; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.client.RestTemplate; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +public class TestVideo1 { + + private static final Logger log = LoggerFactory.getLogger(TestVideo1.class); + private final RestTemplate restTemplate = new RestTemplate(); + + public static void main(String[] args) { + System.out.println("\n" + "=".repeat(60)); + System.out.println("LTX-Video 视频生成客户端"); + System.out.println("=".repeat(60)); + /** + * width height 是否有效 + * 704 480 ✓ + * 640 480 ✓ + * 1024 576 ✓ + */ + String host = "192.168.1.35"; + int port = 8004; + String prompt = "A beautiful young woman with feminine char🐻2pm wearing a bikini, dancing energetically on a sunny beach with golden sand and blue ocean waves, cinematic lighting, high quality, realistic"; + String negativePrompt = "ugly, deformed, blurry, low quality, pixelated, cartoon, anime, watermark, text, logo, multiple people, man, old woman"; + int numFrames = 240; + int height = 480; + int width = 704; + int numInferenceSteps = 50; + double guidanceScale = 7.5; + int fps = 24; + Integer seed = null; + String outputDir = "/home/lizh/python_env/gen_video"; + + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "--host": + host = args[++i]; + break; + case "--port": + port = Integer.parseInt(args[++i]); + break; + case "--prompt": + prompt = args[++i]; + break; + case "--negative-prompt": + // 负提示词参数,暂不使用 + ++i; + break; + case "--num-frames": + numFrames = Integer.parseInt(args[++i]); + break; + case "--height": + height = Integer.parseInt(args[++i]); + break; + case "--width": + width = Integer.parseInt(args[++i]); + break; + case "--num-inference-steps": + numInferenceSteps = Integer.parseInt(args[++i]); + break; + case "--guidance-scale": + guidanceScale = Double.parseDouble(args[++i]); + break; + case "--fps": + fps = Integer.parseInt(args[++i]); + break; + case "--seed": + seed = Integer.parseInt(args[++i]); + break; + case "--output-dir": + outputDir = args[++i]; + break; + } + } + + String baseUrl = "http://" + host + ":" + port; + System.out.println("服务地址: " + baseUrl); + System.out.println("=".repeat(60)); + + TestVideo1 client = new TestVideo1(); + + if (!client.healthCheck(baseUrl)) { + System.err.println("\n✗ 服务不可用,请检查服务状态"); + System.exit(1); + } + + if (prompt == null || prompt.isEmpty()) { + System.err.println("\n✗ 请提供 --prompt 参数"); + System.exit(1); + } + + if (height % 32 != 0) { + System.err.println("\n✗ height 必须是 32 的倍数,当前值: " + height); + System.exit(1); + } + if (width % 32 != 0) { + System.err.println("\n✗ width 必须是 32 的倍数,当前值: " + width); + System.exit(1); + } + + String result = client.generateVideo( + baseUrl, + prompt, + negativePrompt, + numFrames, + height, + width, + numInferenceSteps, + guidanceScale, + fps, + seed, + outputDir + ); + + if (result != null) { + System.exit(0); + } else { + System.exit(1); + } + } + + public boolean healthCheck(String baseUrl) { + try { + String url = baseUrl + "/health"; + ResponseEntity resp = restTemplate.getForEntity(url, String.class); + + if (resp.getStatusCode() == HttpStatus.OK) { + JSONObject data = JSON.parseObject(resp.getBody()); + System.out.println("✓ 服务健康检查通过"); + System.out.println(" 状态: " + data.getString("status")); + System.out.println(" 模型: " + data.getString("model")); + System.out.println(" GPU: " + data.getString("gpu")); + return true; + } else { + System.err.println("✗ 服务响应异常: HTTP " + resp.getStatusCode()); + return false; + } + } catch (ResourceAccessException e) { + System.err.println("✗ 服务连接失败: " + e.getMessage()); + return false; + } catch (Exception e) { + System.err.println("✗ 服务健康检查失败: " + e.getMessage()); + return false; + } + } + + public void listModels(String baseUrl) { + try { + String url = baseUrl + "/v1/models"; + ResponseEntity resp = restTemplate.getForEntity(url, String.class); + + if (resp.getStatusCode() == HttpStatus.OK) { + JSONObject data = JSON.parseObject(resp.getBody()); + System.out.println("可用模型列表:"); + for (Object modelObj : data.getJSONArray("data")) { + JSONObject model = (JSONObject) modelObj; + System.out.println(" - " + model.getString("id")); + } + } else { + System.err.println("✗ 获取模型列表失败: HTTP " + resp.getStatusCode()); + } + } catch (Exception e) { + System.err.println("✗ 获取模型列表失败: " + e.getMessage()); + } + } + + public String generateVideo( + String baseUrl, + String prompt, + String negativePrompt, + int numFrames, + int height, + int width, + int numInferenceSteps, + double guidanceScale, + int fps, + Integer seed, + String outputDir + ) { + Map payload = new HashMap<>(); + payload.put("prompt", prompt); + payload.put("negative_prompt", negativePrompt); + payload.put("num_frames", numFrames); + payload.put("height", height); + payload.put("width", width); + payload.put("num_inference_steps", numInferenceSteps); + payload.put("guidance_scale", guidanceScale); + payload.put("fps", fps); + if (seed != null) { + payload.put("seed", seed); + } + + System.out.println("\n" + "=".repeat(60)); + System.out.println("开始生成视频..."); + System.out.println("=".repeat(60)); + System.out.println("提示词: " + (prompt.length() > 60 ? prompt.substring(0, 60) + "..." : prompt)); + System.out.println("负提示词: " + (negativePrompt.isEmpty() ? "无" : (negativePrompt.length() > 60 ? negativePrompt.substring(0, 60) + "..." : negativePrompt))); + System.out.println("帧数: " + numFrames + ", 分辨率: " + width + "x" + height); + System.out.println("推理步数: " + numInferenceSteps + ", 引导系数: " + guidanceScale); + System.out.println("FPS: " + fps + ", 种子: " + (seed != null ? seed : "随机")); + System.out.println("=".repeat(60)); + + long startTime = System.currentTimeMillis(); + try { + String url = baseUrl + "/v1/video/generate"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(payload, headers); + + ResponseEntity resp = restTemplate.exchange( + url, + HttpMethod.POST, + entity, + String.class + ); + + long elapsed = System.currentTimeMillis() - startTime; + + if (resp.getStatusCode() == HttpStatus.OK) { + JSONObject data = JSON.parseObject(resp.getBody()); + String videoBase64 = data.getString("video_base64"); + Integer seedUsed = data.getInteger("seed"); + Double duration = data.getDouble("duration_seconds"); + + Path outputPath = Paths.get(outputDir); + if (!Files.exists(outputPath)) { + Files.createDirectories(outputPath); + } + + String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); + String filename = "video_" + timestamp + "_seed" + seedUsed + ".mp4"; + Path filepath = outputPath.resolve(filename); + + byte[] videoBytes = Base64.getDecoder().decode(videoBase64); + Files.write(filepath, videoBytes); + + System.out.println("\n✓ 视频生成成功!"); + System.out.println(" 耗时: " + (elapsed / 1000.0) + " 秒"); + System.out.println(" 使用种子: " + seedUsed); + System.out.println(" 视频时长: " + String.format("%.2f", duration) + " 秒"); + System.out.println(" 保存路径: " + filepath.toAbsolutePath()); + return filepath.toString(); + } else { + long elapsedTime = System.currentTimeMillis() - startTime; + System.err.println("\n✗ 视频生成失败: HTTP " + resp.getStatusCode()); + try { + JSONObject errorData = JSON.parseObject(resp.getBody()); + System.err.println(" 错误信息: " + errorData.getString("detail")); + } catch (Exception e) { + System.err.println(" 响应内容: " + resp.getBody()); + } + return null; + } + + } catch (ResourceAccessException e) { + long elapsedTime = System.currentTimeMillis() - startTime; + System.err.println("\n✗ 请求超时 (" + (elapsedTime / 1000.0) + " 秒)"); + return null; + } catch (Exception e) { + long elapsedTime = System.currentTimeMillis() - startTime; + System.err.println("\n✗ 请求失败 (" + (elapsedTime / 1000.0) + " 秒): " + e.getMessage()); + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file