优惠卷计算逻辑
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -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/>
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
18
src/main/sql/consignment_goods.sql
Normal file
18
src/main/sql/consignment_goods.sql
Normal 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='寄售商品表';
|
||||||
@@ -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 '是否VIP:0否 1是',
|
`viptime` datetime DEFAULT NULL COMMENT 'VIP到期时间',
|
||||||
`contract` VARCHAR(512) DEFAULT NULL COMMENT '签约合同文件路径',
|
`is_vip` tinyint NOT NULL DEFAULT '0' COMMENT '是否VIP:0否 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 同步)';
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
47
src/main/sql/member_info.sql
Normal file
47
src/main/sql/member_info.sql
Normal 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='会员表';
|
||||||
68
src/main/sql/数据切割/过程和步骤.sql
Normal file
68
src/main/sql/数据切割/过程和步骤.sql
Normal 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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
282
src/test/java/com/cst/video/TestVideo1.java
Normal file
282
src/test/java/com/cst/video/TestVideo1.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user