优惠卷计算逻辑

This commit is contained in:
2026-07-28 11:56:31 +08:00
parent 42fa82ce07
commit 4d6b3dfe2f
10 changed files with 638 additions and 49 deletions

View File

@@ -10,7 +10,7 @@
</parent> </parent>
<groupId>com.cst</groupId> <groupId>com.cst</groupId>
<artifactId>AIDriverEEBackend</artifactId> <artifactId>AIDriverEEBackend</artifactId>
<version>1.260504.1-SNAPSHOT</version> <version>1.260711.1-SNAPSHOT</version>
<name>Langchain4j-rj</name> <name>Langchain4j-rj</name>
<description>Langchain4j-rj20250803</description> <description>Langchain4j-rj20250803</description>
<url/> <url/>

View File

@@ -121,6 +121,23 @@ public class LbUserCouponController {
return ResponseEntity.badRequest().body(result); return ResponseEntity.badRequest().body(result);
} }
@GetMapping("/calculateMonthlyAmount")
@Operation(summary = "计算用户本月从开始日期到月末的优惠券金额")
public ResponseEntity<Map<String, Object>> 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<String, Object> 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") @GetMapping("/export")
@Operation(summary = "导出用户优惠券Excel", description = "按照分页查询的相同条件导出用户优惠券数据") @Operation(summary = "导出用户优惠券Excel", description = "按照分页查询的相同条件导出用户优惠券数据")
public void export( public void export(

View File

@@ -47,6 +47,16 @@ public interface ILbUserCouponService extends IService<LbUserCoupon> {
*/ */
Map<String, Object> monthlyStatistic(String tenantId, String couponDateStart, String couponDateEnd); Map<String, Object> monthlyStatistic(String tenantId, String couponDateStart, String couponDateEnd);
/**
* 计算用户本月从开始日期到月末的优惠券金额
*
* @param userId 用户ID
* @param userNickname 用户昵称
* @param startDate 开始日期,格式 yyyy-MM-dd
* @return 计算结果
*/
Map<String, Object> calculateUserMonthlyAmount(String userId, String userNickname, String startDate);
/** /**
* 导出用户优惠券Excel * 导出用户优惠券Excel
* *

View File

@@ -668,7 +668,7 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
if (buyResult.success()) { if (buyResult.success()) {
successCount++; successCount++;
successTotalAmount += goods.getTotalMoney().intValue(); successTotalAmount += goods.getTotalMoney().intValue();
Thread.sleep(1000); Thread.sleep(3000);
} else { } else {
failCount++; failCount++;
if (buyResult.orderAlreadyGrabbed()) { if (buyResult.orderAlreadyGrabbed()) {

View File

@@ -32,12 +32,14 @@ import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.time.Duration; import java.time.Duration;
import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -444,6 +446,72 @@ public class LbUserCouponServiceImpl
} }
} }
private List<LbUserCoupon> fillMissingWeekdayRecords(List<LbUserCoupon> records, LocalDate start, LocalDate end) {
if (records == null || records.isEmpty()) {
return records;
}
Map<String, LbUserCoupon> userInfoMap = new LinkedHashMap<>();
Map<String, Set<LocalDate>> 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<LbUserCoupon> result = new ArrayList<>(records);
LocalDateTime now = LocalDateTime.now();
for (Map.Entry<String, LbUserCoupon> entry : userInfoMap.entrySet()) {
String userId = entry.getKey();
LbUserCoupon template = entry.getValue();
Set<LocalDate> 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 @Override
public Map<String, Object> add(LbUserCoupon entity) { public Map<String, Object> add(LbUserCoupon entity) {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
@@ -627,14 +695,34 @@ public class LbUserCouponServiceImpl
queryWrapper.orderByDesc(LbUserCoupon::getCouponAmount); queryWrapper.orderByDesc(LbUserCoupon::getCouponAmount);
Page<LbUserCoupon> page = this.page(new Page<>(current, size), queryWrapper); if (start != null && end != null) {
result.put("success", true); List<LbUserCoupon> allRecords = this.list(queryWrapper);
result.put("message", "查询成功"); List<LbUserCoupon> filledRecords = fillMissingWeekdayRecords(allRecords, start, end);
result.put("data", page.getRecords());
result.put("total", page.getTotal()); int total = filledRecords.size();
result.put("current", page.getCurrent()); int pages = (total + size - 1) / size;
result.put("size", page.getSize()); int fromIndex = (current - 1) * size;
result.put("pages", page.getPages()); int toIndex = Math.min(fromIndex + size, total);
List<LbUserCoupon> 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<LbUserCoupon> 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; return result;
} catch (Exception e) { } catch (Exception e) {
log.error("用户优惠券分页查询异常", e); log.error("用户优惠券分页查询异常", e);
@@ -726,6 +814,63 @@ public class LbUserCouponServiceImpl
} }
} }
@Override
public Map<String, Object> calculateUserMonthlyAmount(String userId, String userNickname, String startDate) {
Map<String, Object> 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<LbUserCoupon> 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<LbUserCoupon> records = this.list(queryWrapper);
BigDecimal totalAmount = records.stream()
.map(r -> r.getCouponAmount() != null ? r.getCouponAmount() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
List<LbUserCoupon> 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 @Override
public void exportExcel(String tenantId, public void exportExcel(String tenantId,
String userId, String userId,

View File

@@ -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='寄售商品表';

View File

@@ -1,46 +1,48 @@
SET NAMES utf8mb4;
CREATE TABLE `lb_user` ( CREATE TABLE `lb_user` (
`id` BIGINT NOT NULL COMMENT '用户ID外部系统主键', `id` bigint NOT NULL COMMENT '用户ID外部系统主键',
`pid` BIGINT DEFAULT NULL COMMENT '上级用户ID', `pid` bigint DEFAULT NULL COMMENT '上级用户ID',
`username` VARCHAR(64) DEFAULT NULL COMMENT '用户名(常为手机号)', `tenant_id` varchar(64) DEFAULT NULL COMMENT '租户id',
`nickname` VARCHAR(100) DEFAULT NULL COMMENT '昵称', `username` varchar(64) DEFAULT NULL COMMENT '用户名(常为手机号)',
`mobile` VARCHAR(32) DEFAULT NULL COMMENT '手机号', `nickname` varchar(100) DEFAULT NULL COMMENT '昵称',
`password` VARCHAR(64) DEFAULT NULL COMMENT '密码(加密后)', `mobile` varchar(32) DEFAULT NULL COMMENT '手机号',
`salt` VARCHAR(32) DEFAULT NULL COMMENT '密码', `password` varchar(64) DEFAULT NULL COMMENT '密码(加密后)',
`sex` VARCHAR(8) DEFAULT NULL COMMENT '性别', `salt` varchar(32) DEFAULT NULL COMMENT '密码盐',
`avatar` VARCHAR(512) DEFAULT NULL COMMENT '头像路径', `sex` varchar(8) DEFAULT NULL COMMENT '性别',
`invite` VARCHAR(32) DEFAULT NULL COMMENT '邀请码', `avatar` varchar(512) DEFAULT NULL COMMENT '头像路径',
`level` INT NOT NULL DEFAULT 0 COMMENT '等级', `invite` varchar(32) DEFAULT NULL COMMENT '邀请码',
`birthday` DATE DEFAULT NULL COMMENT '生日', `level` int NOT NULL DEFAULT '0' COMMENT '等级',
`money` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '余额', `birthday` date DEFAULT NULL COMMENT '生日',
`coupon` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '优惠券金', `money` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '',
`self_bonus` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '自购奖金', `coupon` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '优惠券金额',
`share_bonus` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '分享奖金', `self_bonus` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '自购奖金',
`score` INT NOT NULL DEFAULT 0 COMMENT '', `share_bonus` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '享奖金',
`last_time` DATETIME DEFAULT NULL COMMENT '最后登录时间', `score` int NOT NULL DEFAULT '0' COMMENT '积分',
`last_ip` VARCHAR(45) DEFAULT NULL COMMENT '最后登录IP', `last_time` datetime DEFAULT NULL COMMENT '最后登录时间',
`join_time` DATETIME DEFAULT NULL COMMENT '注册时间', `last_ip` varchar(45) DEFAULT NULL COMMENT '最后登录IP',
`join_ip` VARCHAR(45) DEFAULT NULL COMMENT '注册IP', `join_time` datetime DEFAULT NULL COMMENT '注册时间',
`token` VARCHAR(255) DEFAULT NULL COMMENT '登录令牌', `join_ip` varchar(45) DEFAULT NULL COMMENT '注册IP',
`created_at` DATETIME DEFAULT NULL COMMENT '创建时间', `token` varchar(255) DEFAULT NULL COMMENT '登录令牌',
`updated_at` DATETIME DEFAULT NULL COMMENT '更新时间', `created_at` datetime DEFAULT NULL COMMENT '创建时间',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态', `updated_at` datetime DEFAULT NULL COMMENT '更新时间',
`viptime` DATETIME DEFAULT NULL COMMENT 'VIP到期时间', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态',
`is_vip` TINYINT NOT NULL DEFAULT 0 COMMENT '是否VIP0否 1是', `viptime` datetime DEFAULT NULL COMMENT 'VIP到期时间',
`contract` VARCHAR(512) DEFAULT NULL COMMENT '签约合同文件路径', `is_vip` tinyint NOT NULL DEFAULT '0' COMMENT '是否VIP0否 1是',
`max_order` INT NOT NULL DEFAULT 0 COMMENT '最大订单数', `contract` varchar(512) DEFAULT NULL COMMENT '签约合同文件路径',
`is_resell` TINYINT NOT NULL DEFAULT 0 COMMENT '是否可转卖0否 1是', `max_order` int NOT NULL DEFAULT '0' COMMENT '最大订单数',
`yesterday_sell_count` INT NOT NULL DEFAULT 0 COMMENT '昨日卖出笔数', `is_resell` tinyint NOT NULL DEFAULT '0' COMMENT '是否可转卖0否 1是',
`today_buy_count` INT NOT NULL DEFAULT 0 COMMENT '今日买入笔数', `yesterday_sell_count` int NOT NULL DEFAULT '0' COMMENT '昨日卖出笔数',
`today_buy_total` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '今日买入总额', `today_buy_count` int NOT NULL DEFAULT '0' COMMENT '今日买入笔数',
`today_sell_total` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '今日卖出总额', `today_buy_total` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '今日买入总额',
`poor` TINYINT NOT NULL DEFAULT 0 COMMENT '贫困标识/标记', `today_sell_total` decimal(12,3) NOT NULL DEFAULT '0.000' COMMENT '今日卖出总额',
`pname` VARCHAR(100) DEFAULT NULL COMMENT '上级用户昵称(接口关联字段,可选)', `poor` tinyint NOT NULL DEFAULT '0' COMMENT '贫困标识/标记',
`pname` varchar(100) DEFAULT NULL COMMENT '上级用户昵称(接口关联字段,可选)',
`pmobile` varchar(32) DEFAULT NULL COMMENT '上级用户的电话',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `idx_pid` (`pid`), KEY `idx_pid` (`pid`),
KEY `idx_mobile` (`mobile`), KEY `idx_mobile` (`mobile`),
KEY `idx_username` (`username`), KEY `idx_username` (`username`),
KEY `idx_join_time` (`join_time`), KEY `idx_join_time` (`join_time`),
KEY `idx_updated_at` (`updated_at`) 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 同步)';

View File

@@ -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='会员表';

View File

@@ -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;

View File

@@ -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<String> 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<String> 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<String, Object> 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<Map<String, Object>> entity = new HttpEntity<>(payload, headers);
ResponseEntity<String> 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;
}
}
}