chore: update project files
This commit is contained in:
188
demo/src/main/java/com/example/demo/config/OpenApiConfig.java
Normal file
188
demo/src/main/java/com/example/demo/config/OpenApiConfig.java
Normal file
@@ -0,0 +1,188 @@
|
||||
package com.example.demo.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* OpenAPI (Swagger) 配置类
|
||||
* 访问地址: http://localhost:8080/swagger-ui.html
|
||||
* API文档JSON: http://localhost:8080/v3/api-docs
|
||||
*/
|
||||
@Configuration
|
||||
public class OpenApiConfig {
|
||||
|
||||
/**
|
||||
* OpenAPI 主配置
|
||||
*/
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("AIGC平台 API 文档")
|
||||
.version("1.0.0")
|
||||
.description("AIGC平台后端API接口文档,包含用户认证、视频生成、支付、订单管理等模块")
|
||||
.contact(new Contact()
|
||||
.name("AIGC平台开发团队")
|
||||
.email("support@vionow.com"))
|
||||
.license(new License()
|
||||
.name("Apache 2.0")
|
||||
.url("https://www.apache.org/licenses/LICENSE-2.0.html")))
|
||||
.servers(Arrays.asList(
|
||||
new Server().url("http://localhost:8080").description("本地开发环境"),
|
||||
new Server().url("http://172.22.0.1:8080").description("内网环境"),
|
||||
new Server().url("https://vionow.com").description("生产环境")
|
||||
))
|
||||
.addSecurityItem(new SecurityRequirement().addList("Bearer Authentication"))
|
||||
.components(new io.swagger.v3.oas.models.Components()
|
||||
.addSecuritySchemes("Bearer Authentication", new SecurityScheme()
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")
|
||||
.description("JWT认证,格式: Bearer {token}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有API分组
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi allApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("all")
|
||||
.displayName("所有API")
|
||||
.pathsToMatch("/api/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证相关API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi authApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("auth")
|
||||
.displayName("认证授权")
|
||||
.pathsToMatch("/api/auth/**", "/api/verification/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户作品API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi userWorkApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("user-works")
|
||||
.displayName("用户作品")
|
||||
.pathsToMatch("/api/works/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频生成API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi videoGenerationApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("video-generation")
|
||||
.displayName("视频生成")
|
||||
.pathsToMatch("/api/text-to-video/**", "/api/image-to-video/**", "/api/storyboard-video/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付相关API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi paymentApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("payment")
|
||||
.displayName("支付管理")
|
||||
.pathsToMatch("/api/payments/**", "/api/orders/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员管理API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi memberApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("member")
|
||||
.displayName("会员管理")
|
||||
.pathsToMatch("/api/members/**", "/api/subscription/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 积分系统API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi pointsApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("points")
|
||||
.displayName("积分系统")
|
||||
.pathsToMatch("/api/points/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务管理API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi taskApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("tasks")
|
||||
.displayName("任务管理")
|
||||
.pathsToMatch("/api/tasks/**", "/api/queue/**", "/api/task-status/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理后台API
|
||||
* 注意:这些API需要ADMIN权限,但文档生成不需要权限验证
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi adminApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("admin")
|
||||
.displayName("管理后台")
|
||||
.pathsToMatch("/api/admin/**", "/api/dashboard/**", "/api/analytics/**", "/api/cleanup/**")
|
||||
// 在文档生成时,不验证权限
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统设置API
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi settingsApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("settings")
|
||||
.displayName("系统设置")
|
||||
.pathsToMatch("/api/settings/**", "/api/api-keys/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 公共API(无需认证)
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi publicApis() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("public")
|
||||
.displayName("公共接口")
|
||||
.pathsToMatch("/api/public/**", "/api/test/**")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.example.demo.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
import org.springframework.web.util.ContentCachingResponseWrapper;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 请求日志过滤器
|
||||
* 用于记录请求和响应的详细信息,特别是在出现 JSON 解析错误时帮助调试
|
||||
*/
|
||||
@Component
|
||||
public class RequestLoggingFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RequestLoggingFilter.class);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
// 包装请求和响应以便读取内容
|
||||
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
|
||||
ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);
|
||||
|
||||
try {
|
||||
// 继续过滤链
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} catch (Exception e) {
|
||||
// 如果出现异常,记录请求详情
|
||||
logRequestDetails(wrappedRequest, e);
|
||||
throw e;
|
||||
} finally {
|
||||
// 记录响应状态
|
||||
if (wrappedResponse.getStatus() >= 400) {
|
||||
// 只有在出现错误时才记录详细的请求信息
|
||||
logRequestDetails(wrappedRequest, null);
|
||||
}
|
||||
|
||||
// 复制响应内容到原始响应
|
||||
wrappedResponse.copyBodyToResponse();
|
||||
}
|
||||
}
|
||||
|
||||
private void logRequestDetails(ContentCachingRequestWrapper request, Exception exception) {
|
||||
try {
|
||||
String uri = request.getRequestURI();
|
||||
String method = request.getMethod();
|
||||
|
||||
// 只记录POST/PUT/PATCH请求的详情
|
||||
if ("POST".equals(method) || "PUT".equals(method) || "PATCH".equals(method)) {
|
||||
logger.error("=== 请求详情 ({}出现问题) ===", exception != null ? "捕获异常" : "响应错误");
|
||||
logger.error("URI: {} {}", method, uri);
|
||||
logger.error("Content-Type: {}", request.getContentType());
|
||||
logger.error("Content-Length: {}", request.getContentLength());
|
||||
|
||||
// 获取请求体内容
|
||||
byte[] content = request.getContentAsByteArray();
|
||||
if (content.length > 0) {
|
||||
String body = getContentAsString(content, request.getCharacterEncoding());
|
||||
logger.error("请求体内容 (前1000字符):");
|
||||
logger.error("{}", body.length() > 1000 ? body.substring(0, 1000) + "..." : body);
|
||||
|
||||
// 检查是否包含可疑的反斜杠
|
||||
if (body.contains("\\")) {
|
||||
logger.error("⚠️ 警告:请求体中包含反斜杠字符,这可能导致 JSON 解析错误");
|
||||
// 显示反斜杠周围的上下文
|
||||
int index = body.indexOf("\\");
|
||||
if (index >= 0) {
|
||||
int start = Math.max(0, index - 20);
|
||||
int end = Math.min(body.length(), index + 20);
|
||||
logger.error("反斜杠位置附近的内容: [{}]", body.substring(start, end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
logger.error("异常: {}", exception.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("记录请求详情时出错", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getContentAsString(byte[] content, String charset) {
|
||||
try {
|
||||
return new String(content, charset != null ? charset : "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return "[无法解码内容]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,39 @@ public class SecurityConfig {
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态,使用JWT
|
||||
)
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/login", "/register", "/api/public/**", "/api/auth/**", "/api/verification/**", "/api/email/**", "/api/tencent/**", "/api/test/**", "/api/polling/**", "/api/diagnostic/**", "/api/polling-diagnostic/**", "/api/monitor/**", "/css/**", "/js/**", "/h2-console/**").permitAll()
|
||||
// Swagger/OpenAPI 路径 - 必须放在最前面,完全公开访问
|
||||
.requestMatchers(
|
||||
"/swagger-ui/**",
|
||||
"/swagger-ui.html",
|
||||
"/v3/api-docs/**",
|
||||
"/v3/api-docs",
|
||||
"/api-docs/**",
|
||||
"/swagger-resources/**",
|
||||
"/webjars/**",
|
||||
"/configuration/ui",
|
||||
"/configuration/security",
|
||||
"/swagger-config",
|
||||
"/api/swagger-config"
|
||||
).permitAll()
|
||||
// 公共路径
|
||||
.requestMatchers(
|
||||
"/login",
|
||||
"/register",
|
||||
"/api/public/**",
|
||||
"/api/auth/**",
|
||||
"/api/verification/**",
|
||||
"/api/email/**",
|
||||
"/api/tencent/**",
|
||||
"/api/test/**",
|
||||
"/api/polling/**",
|
||||
"/api/diagnostic/**",
|
||||
"/api/polling-diagnostic/**",
|
||||
"/api/monitor/**",
|
||||
"/api/health/**",
|
||||
"/css/**",
|
||||
"/js/**",
|
||||
"/h2-console/**"
|
||||
).permitAll()
|
||||
.requestMatchers("/api/orders/stats").permitAll() // 统计接口允许匿名访问
|
||||
.requestMatchers("/api/orders/**").authenticated() // 订单接口需要认证
|
||||
.requestMatchers("/api/payments/alipay/notify", "/api/payments/alipay/return").permitAll() // 支付宝回调接口允许匿名访问(外部调用)
|
||||
@@ -53,10 +85,13 @@ public class SecurityConfig {
|
||||
.requestMatchers("/api/admin/**").hasRole("ADMIN") // 管理员API需要管理员权限
|
||||
.requestMatchers("/settings", "/settings/**").hasRole("ADMIN")
|
||||
.requestMatchers("/users/**").hasRole("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
.formLogin(form -> form
|
||||
.loginPage("/login")
|
||||
// 使用email作为表单登录的用户名参数(账号即邮箱)
|
||||
.usernameParameter("email")
|
||||
.passwordParameter("password")
|
||||
.defaultSuccessUrl("/", true)
|
||||
.permitAll()
|
||||
)
|
||||
@@ -88,23 +123,58 @@ public class SecurityConfig {
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
// 允许前端开发服务器和ngrok域名
|
||||
configuration.setAllowedOrigins(Arrays.asList(
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000",
|
||||
"http://localhost:5173",
|
||||
"http://127.0.0.1:5173"
|
||||
));
|
||||
|
||||
// 面向海外用户,使用最宽松的CORS配置
|
||||
// 使用allowedOriginPatterns支持通配符(当allowCredentials=true时必须使用patterns而不是origins)
|
||||
configuration.setAllowedOriginPatterns(Arrays.asList(
|
||||
// 允许所有HTTP和HTTPS来源(任意域名、任意IP、任意端口)
|
||||
"http://*:*",
|
||||
"https://*:*",
|
||||
"http://*",
|
||||
"https://*",
|
||||
"http://*.*",
|
||||
"https://*.*",
|
||||
"http://*.*.*",
|
||||
"https://*.*.*",
|
||||
|
||||
// 本地开发环境
|
||||
"http://localhost:*",
|
||||
"http://127.0.0.1:*",
|
||||
"http://0.0.0.0:*",
|
||||
|
||||
// 常见开发工具和隧道服务
|
||||
"https://*.ngrok.io",
|
||||
"https://*.ngrok-free.app"
|
||||
"https://*.ngrok-free.app",
|
||||
"https://*.cloudflare.com",
|
||||
"https://*.vercel.app",
|
||||
"https://*.netlify.app",
|
||||
"https://*.herokuapp.com",
|
||||
"https://*.github.io",
|
||||
"https://*.gitlab.io",
|
||||
|
||||
// 生产域名(如果有)
|
||||
"https://vionow.com",
|
||||
"https://www.vionow.com",
|
||||
"http://vionow.com",
|
||||
"http://www.vionow.com",
|
||||
"https://*.vionow.com"
|
||||
));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
|
||||
// 允许所有HTTP方法
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD", "TRACE", "CONNECT"));
|
||||
|
||||
// 允许所有请求头
|
||||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||||
|
||||
// 允许携带凭证(cookies, authorization headers等)
|
||||
configuration.setAllowCredentials(true);
|
||||
configuration.setExposedHeaders(Arrays.asList("Authorization"));
|
||||
configuration.setMaxAge(3600L); // 预检请求缓存时间
|
||||
|
||||
|
||||
// 暴露所有响应头给客户端
|
||||
configuration.setExposedHeaders(Arrays.asList("*"));
|
||||
|
||||
// 预检请求缓存时间(1小时,减少预检请求频率)
|
||||
configuration.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.example.demo.model.Payment;
|
||||
import com.example.demo.model.PaymentStatus;
|
||||
import com.example.demo.repository.PaymentRepository;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 支付宝回调接口控制器
|
||||
* 参考 IJPay 实现,处理支付宝异步通知和同步返回
|
||||
*
|
||||
* @author Generated
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/payments/alipay")
|
||||
public class AlipayCallbackController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AlipayCallbackController.class);
|
||||
|
||||
@Autowired
|
||||
private PaymentRepository paymentRepository;
|
||||
|
||||
@Autowired
|
||||
private com.example.demo.config.PaymentConfig.AliPayConfig aliPayConfig;
|
||||
|
||||
/**
|
||||
* 支付宝异步通知接口
|
||||
* 支付宝服务器会通过POST方式调用此接口,通知支付结果
|
||||
*
|
||||
* 参考 IJPay 实现:
|
||||
* - 使用 AlipaySignature.rsaCheckV1() 验证签名
|
||||
* - 处理重复通知(去重)
|
||||
* - 更新订单状态
|
||||
* - 返回 "success" 或 "failure"
|
||||
*
|
||||
* @param request HTTP请求对象
|
||||
* @return "success" 表示处理成功,"failure" 表示处理失败
|
||||
*/
|
||||
@PostMapping(value = "/notify", produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
public String notifyUrl(HttpServletRequest request) {
|
||||
logger.info("========== 收到支付宝异步通知 ==========");
|
||||
logger.info("请求方法: {}", request.getMethod());
|
||||
logger.info("请求URL: {}", request.getRequestURL());
|
||||
logger.info("Content-Type: {}", request.getContentType());
|
||||
|
||||
try {
|
||||
// 获取支付宝POST过来的反馈信息
|
||||
Map<String, String> params = getRequestParams(request);
|
||||
|
||||
// 打印所有参数(用于调试)
|
||||
logger.info("支付宝异步通知参数:");
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
logger.info(" {} = {}", entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
boolean verifyResult = AlipaySignature.rsaCheckV1(
|
||||
params,
|
||||
aliPayConfig.getPublicKey(),
|
||||
aliPayConfig.getCharset() != null ? aliPayConfig.getCharset() : "UTF-8",
|
||||
aliPayConfig.getSignType() != null ? aliPayConfig.getSignType() : "RSA2"
|
||||
);
|
||||
|
||||
if (!verifyResult) {
|
||||
logger.error("========== 支付宝异步通知签名验证失败 ==========");
|
||||
return "failure";
|
||||
}
|
||||
|
||||
logger.info("========== 支付宝异步通知签名验证成功 ==========");
|
||||
|
||||
// 获取关键参数
|
||||
String tradeStatus = params.get("trade_status");
|
||||
String outTradeNo = params.get("out_trade_no");
|
||||
String tradeNo = params.get("trade_no");
|
||||
String totalAmount = params.get("total_amount");
|
||||
String appId = params.get("app_id");
|
||||
|
||||
logger.info("订单号: {}, 交易号: {}, 交易状态: {}, 金额: {}, 应用ID: {}",
|
||||
outTradeNo, tradeNo, tradeStatus, totalAmount, appId);
|
||||
|
||||
// 查找支付记录
|
||||
Optional<Payment> paymentOpt = paymentRepository.findByOrderId(outTradeNo);
|
||||
if (!paymentOpt.isPresent()) {
|
||||
logger.error("支付记录不存在: {}", outTradeNo);
|
||||
return "failure";
|
||||
}
|
||||
|
||||
Payment payment = paymentOpt.get();
|
||||
|
||||
// 处理重复通知(去重)
|
||||
// 如果订单已经是成功状态,且本次通知也是成功,则认为是重复通知
|
||||
if (payment.getStatus() == PaymentStatus.SUCCESS &&
|
||||
("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus))) {
|
||||
logger.info("收到重复的成功通知,订单号: {}, 已处理过,直接返回success", outTradeNo);
|
||||
return "success";
|
||||
}
|
||||
|
||||
// 根据交易状态更新支付记录
|
||||
boolean shouldUpdateOrder = false;
|
||||
switch (tradeStatus) {
|
||||
case "TRADE_SUCCESS":
|
||||
case "TRADE_FINISHED":
|
||||
// 交易成功
|
||||
if (payment.getStatus() != PaymentStatus.SUCCESS) {
|
||||
payment.setStatus(PaymentStatus.SUCCESS);
|
||||
payment.setExternalTransactionId(tradeNo);
|
||||
payment.setPaidAt(LocalDateTime.now());
|
||||
shouldUpdateOrder = true;
|
||||
logger.info("订单支付成功: {}", outTradeNo);
|
||||
}
|
||||
break;
|
||||
|
||||
case "TRADE_CLOSED":
|
||||
// 交易关闭(未付款交易超时关闭,或支付完成后全额退款)
|
||||
if (payment.getStatus() != PaymentStatus.CANCELLED) {
|
||||
payment.setStatus(PaymentStatus.CANCELLED);
|
||||
shouldUpdateOrder = true;
|
||||
logger.info("订单已关闭: {}", outTradeNo);
|
||||
}
|
||||
break;
|
||||
|
||||
case "WAIT_BUYER_PAY":
|
||||
// 交易创建,等待买家付款
|
||||
if (payment.getStatus() != PaymentStatus.PROCESSING) {
|
||||
payment.setStatus(PaymentStatus.PROCESSING);
|
||||
shouldUpdateOrder = true;
|
||||
logger.info("订单等待付款: {}", outTradeNo);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.warn("未知的交易状态: {}, 订单号: {}", tradeStatus, outTradeNo);
|
||||
break;
|
||||
}
|
||||
|
||||
// 保存支付记录
|
||||
if (shouldUpdateOrder) {
|
||||
paymentRepository.save(payment);
|
||||
logger.info("支付记录更新成功: 订单号={}, 状态={}", outTradeNo, payment.getStatus());
|
||||
|
||||
// TODO: 这里可以触发订单状态更新、发送通知等业务逻辑
|
||||
// 例如:更新订单状态、扣除用户资源点、发送通知等
|
||||
}
|
||||
|
||||
logger.info("========== 支付宝异步通知处理完成 ==========");
|
||||
return "success";
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("========== 处理支付宝异步通知异常 ==========", e);
|
||||
return "failure";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付宝同步返回接口
|
||||
* 用户支付完成后,支付宝会跳转到此页面(GET请求)
|
||||
*
|
||||
* 参考 IJPay 实现:
|
||||
* - 使用 AlipaySignature.rsaCheckV1() 验证签名
|
||||
* - 验证成功后可以跳转到支付成功页面
|
||||
*
|
||||
* @param request HTTP请求对象
|
||||
* @return 支付结果信息
|
||||
*/
|
||||
@GetMapping("/return")
|
||||
public Map<String, Object> returnUrl(HttpServletRequest request) {
|
||||
logger.info("========== 收到支付宝同步返回 ==========");
|
||||
logger.info("请求方法: {}", request.getMethod());
|
||||
logger.info("请求URL: {}", request.getRequestURL());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 获取支付宝GET过来的反馈信息
|
||||
Map<String, String> params = getRequestParams(request);
|
||||
|
||||
// 打印所有参数(用于调试)
|
||||
logger.info("支付宝同步返回参数:");
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
logger.info(" {} = {}", entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
boolean verifyResult = AlipaySignature.rsaCheckV1(
|
||||
params,
|
||||
aliPayConfig.getPublicKey(),
|
||||
aliPayConfig.getCharset() != null ? aliPayConfig.getCharset() : "UTF-8",
|
||||
aliPayConfig.getSignType() != null ? aliPayConfig.getSignType() : "RSA2"
|
||||
);
|
||||
|
||||
if (!verifyResult) {
|
||||
logger.error("========== 支付宝同步返回签名验证失败 ==========");
|
||||
result.put("success", false);
|
||||
result.put("message", "签名验证失败");
|
||||
return result;
|
||||
}
|
||||
|
||||
logger.info("========== 支付宝同步返回签名验证成功 ==========");
|
||||
|
||||
// 获取关键参数
|
||||
String outTradeNo = params.get("out_trade_no");
|
||||
String tradeNo = params.get("trade_no");
|
||||
String totalAmount = params.get("total_amount");
|
||||
|
||||
logger.info("订单号: {}, 交易号: {}, 金额: {}", outTradeNo, tradeNo, totalAmount);
|
||||
|
||||
// 查找支付记录
|
||||
Optional<Payment> paymentOpt = paymentRepository.findByOrderId(outTradeNo);
|
||||
if (!paymentOpt.isPresent()) {
|
||||
logger.error("支付记录不存在: {}", outTradeNo);
|
||||
result.put("success", false);
|
||||
result.put("message", "支付记录不存在");
|
||||
return result;
|
||||
}
|
||||
|
||||
Payment payment = paymentOpt.get();
|
||||
|
||||
// 如果状态还是待支付,更新为处理中(实际状态应该由异步通知更新)
|
||||
if (payment.getStatus() == PaymentStatus.PENDING) {
|
||||
payment.setStatus(PaymentStatus.PROCESSING);
|
||||
payment.setExternalTransactionId(tradeNo);
|
||||
paymentRepository.save(payment);
|
||||
logger.info("支付记录状态已更新为处理中: {}", outTradeNo);
|
||||
}
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "支付成功");
|
||||
result.put("orderId", outTradeNo);
|
||||
result.put("tradeNo", tradeNo);
|
||||
result.put("amount", totalAmount);
|
||||
result.put("paymentStatus", payment.getStatus().name());
|
||||
|
||||
logger.info("========== 支付宝同步返回处理完成 ==========");
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("========== 处理支付宝同步返回异常 ==========", e);
|
||||
result.put("success", false);
|
||||
result.put("message", "处理失败: " + e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从HttpServletRequest中获取所有参数
|
||||
* 兼容GET和POST请求,支持form-urlencoded格式
|
||||
*
|
||||
* 注意:Spring Boot 3 使用 jakarta.servlet,而 IJPay 可能使用 javax.servlet
|
||||
* 所以这里手动解析参数,而不是直接使用 AliPayApi.toMap(request)
|
||||
*
|
||||
* @param request HTTP请求对象
|
||||
* @return 参数Map
|
||||
*/
|
||||
private Map<String, String> getRequestParams(HttpServletRequest request) throws IOException {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
|
||||
// 获取URL参数(GET请求)
|
||||
request.getParameterMap().forEach((key, values) -> {
|
||||
if (values != null && values.length > 0) {
|
||||
params.put(key, values[0]);
|
||||
}
|
||||
});
|
||||
|
||||
// 如果是POST请求,尝试从请求体获取参数
|
||||
if ("POST".equalsIgnoreCase(request.getMethod())) {
|
||||
try (BufferedReader reader = request.getReader()) {
|
||||
StringBuilder body = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
body.append(line);
|
||||
}
|
||||
|
||||
// 解析form-urlencoded格式的请求体
|
||||
if (body.length() > 0) {
|
||||
String bodyStr = body.toString();
|
||||
if (bodyStr.contains("=")) {
|
||||
String[] pairs = bodyStr.split("&");
|
||||
for (String pair : pairs) {
|
||||
String[] keyValue = pair.split("=", 2);
|
||||
if (keyValue.length == 2) {
|
||||
try {
|
||||
String key = java.net.URLDecoder.decode(keyValue[0], "UTF-8");
|
||||
String value = java.net.URLDecoder.decode(keyValue[1], "UTF-8");
|
||||
// POST参数会覆盖GET参数(如果存在)
|
||||
params.put(key, value);
|
||||
} catch (Exception e) {
|
||||
logger.warn("解析参数失败: {}", pair, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.example.demo.controller;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -22,7 +21,6 @@ import com.alipay.api.domain.AlipayTradePrecreateModel;
|
||||
import com.alipay.api.domain.AlipayTradeQueryModel;
|
||||
import com.alipay.api.domain.AlipayTradeRefundModel;
|
||||
import com.alipay.api.domain.AlipayTradeWapPayModel;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.ijpay.alipay.AliPayApi;
|
||||
|
||||
/**
|
||||
@@ -217,71 +215,9 @@ public class AlipayController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付同步回调
|
||||
* 支付同步回调和异步回调已移至 AlipayCallbackController
|
||||
* 请使用 /api/payments/alipay/return 和 /api/payments/alipay/notify
|
||||
*
|
||||
* @deprecated 此方法已移除,请使用 AlipayCallbackController
|
||||
*/
|
||||
@GetMapping("/return")
|
||||
public ResponseEntity<Map<String, Object>> returnUrl(HttpServletRequest request) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
try {
|
||||
Map<String, String> params = AliPayApi.toMap(request);
|
||||
logger.info("支付宝同步回调参数: {}", params);
|
||||
|
||||
boolean verifyResult = AlipaySignature.rsaCertCheckV1(params,
|
||||
aliPayConfig.getAliPayCertPath(), "UTF-8", "RSA2");
|
||||
|
||||
if (verifyResult) {
|
||||
response.put("success", true);
|
||||
response.put("message", "支付成功");
|
||||
logger.info("支付宝同步回调验证成功");
|
||||
} else {
|
||||
response.put("success", false);
|
||||
response.put("message", "支付验证失败");
|
||||
logger.warn("支付宝同步回调验证失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("支付宝同步回调处理失败", e);
|
||||
response.put("success", false);
|
||||
response.put("message", "处理失败: " + e.getMessage());
|
||||
}
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付异步回调
|
||||
*/
|
||||
@PostMapping("/notify")
|
||||
public String notifyUrl(HttpServletRequest request) {
|
||||
try {
|
||||
Map<String, String> params = AliPayApi.toMap(request);
|
||||
logger.info("支付宝异步回调参数: {}", params);
|
||||
|
||||
boolean verifyResult = AlipaySignature.rsaCertCheckV1(params,
|
||||
aliPayConfig.getAliPayCertPath(), "UTF-8", "RSA2");
|
||||
|
||||
if (verifyResult) {
|
||||
// TODO: 处理支付成功业务逻辑
|
||||
String outTradeNo = params.get("out_trade_no");
|
||||
String tradeNo = params.get("trade_no");
|
||||
String tradeStatus = params.get("trade_status");
|
||||
|
||||
logger.info("支付宝异步回调验证成功: outTradeNo={}, tradeNo={}, status={}",
|
||||
outTradeNo, tradeNo, tradeStatus);
|
||||
|
||||
// 处理支付成功逻辑
|
||||
if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
|
||||
// 更新订单状态为已支付
|
||||
// 这里可以调用订单服务更新状态
|
||||
logger.info("订单支付成功: {}", outTradeNo);
|
||||
}
|
||||
|
||||
return "success";
|
||||
} else {
|
||||
logger.warn("支付宝异步回调验证失败");
|
||||
return "failure";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("支付宝异步回调处理失败", e);
|
||||
return "failure";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.example.demo.service.ApiResponseHandler;
|
||||
import com.example.demo.util.JwtUtils;
|
||||
|
||||
/**
|
||||
* API测试控制器
|
||||
* 演示如何使用改进的API调用和返回值处理
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/test")
|
||||
public class ApiTestController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiTestController.class);
|
||||
|
||||
@Autowired
|
||||
private ApiResponseHandler apiResponseHandler;
|
||||
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
@Value("${ai.api.base-url:http://116.62.4.26:8081}")
|
||||
private String aiApiBaseUrl;
|
||||
|
||||
@Value("${ai.api.key:ak_5f13ec469e6047d5b8155c3cc91350e2}")
|
||||
private String aiApiKey;
|
||||
|
||||
/**
|
||||
* 获取视频列表 - 演示GET API调用
|
||||
*/
|
||||
@GetMapping("/videos")
|
||||
public ResponseEntity<Map<String, Object>> getVideos(
|
||||
@RequestHeader("Authorization") String token) {
|
||||
|
||||
try {
|
||||
// 验证用户身份
|
||||
String username = extractUsernameFromToken(token);
|
||||
if (username == null) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(apiResponseHandler.createErrorResponse("用户未登录"));
|
||||
}
|
||||
|
||||
// 调用API获取视频列表
|
||||
Map<String, Object> result = apiResponseHandler.getVideoList(aiApiKey, aiApiBaseUrl);
|
||||
|
||||
return ResponseEntity.ok(apiResponseHandler.createSuccessResponse(result));
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取视频列表失败", e);
|
||||
return ResponseEntity.status(500)
|
||||
.body(apiResponseHandler.createErrorResponse("获取视频列表失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务状态 - 演示带参数的GET API调用
|
||||
*/
|
||||
@GetMapping("/tasks/{taskId}/status")
|
||||
public ResponseEntity<Map<String, Object>> getTaskStatus(
|
||||
@PathVariable String taskId,
|
||||
@RequestHeader("Authorization") String token) {
|
||||
|
||||
try {
|
||||
// 验证用户身份
|
||||
String username = extractUsernameFromToken(token);
|
||||
if (username == null) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(apiResponseHandler.createErrorResponse("用户未登录"));
|
||||
}
|
||||
|
||||
// 调用API获取任务状态
|
||||
Map<String, Object> result = apiResponseHandler.getTaskStatus(taskId, aiApiKey, aiApiBaseUrl);
|
||||
|
||||
return ResponseEntity.ok(apiResponseHandler.createSuccessResponse(result));
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取任务状态失败", e);
|
||||
return ResponseEntity.status(500)
|
||||
.body(apiResponseHandler.createErrorResponse("获取任务状态失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交测试任务 - 演示POST API调用
|
||||
*/
|
||||
@PostMapping("/submit-task")
|
||||
public ResponseEntity<Map<String, Object>> submitTestTask(
|
||||
@RequestBody Map<String, Object> request,
|
||||
@RequestHeader("Authorization") String token) {
|
||||
|
||||
try {
|
||||
// 验证用户身份
|
||||
String username = extractUsernameFromToken(token);
|
||||
if (username == null) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(apiResponseHandler.createErrorResponse("用户未登录"));
|
||||
}
|
||||
|
||||
// 准备请求参数
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("modelName", request.getOrDefault("modelName", "default-model"));
|
||||
requestBody.put("prompt", request.get("prompt"));
|
||||
requestBody.put("aspectRatio", request.getOrDefault("aspectRatio", "16:9"));
|
||||
requestBody.put("imageToVideo", request.getOrDefault("imageToVideo", false));
|
||||
|
||||
// 调用API提交任务
|
||||
String url = aiApiBaseUrl + "/user/ai/tasks/submit";
|
||||
Map<String, Object> result = apiResponseHandler.callApi(url, aiApiKey, requestBody);
|
||||
|
||||
return ResponseEntity.ok(apiResponseHandler.createSuccessResponse(result));
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("提交测试任务失败", e);
|
||||
return ResponseEntity.status(500)
|
||||
.body(apiResponseHandler.createErrorResponse("提交测试任务失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用外部API - 演示原始Unirest调用
|
||||
*/
|
||||
@GetMapping("/external-api")
|
||||
public ResponseEntity<Map<String, Object>> callExternalApi(
|
||||
@RequestHeader("Authorization") String token) {
|
||||
|
||||
try {
|
||||
// 验证用户身份
|
||||
String username = extractUsernameFromToken(token);
|
||||
if (username == null) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(apiResponseHandler.createErrorResponse("用户未登录"));
|
||||
}
|
||||
|
||||
// 使用您提供的代码模式
|
||||
String url = aiApiBaseUrl + "/v1/videos/";
|
||||
|
||||
// 这里演示您提到的代码模式
|
||||
// Unirest.setTimeouts(0, 0);
|
||||
// HttpResponse<String> response = Unirest.get(url)
|
||||
// .header("Authorization", "Bearer " + aiApiKey)
|
||||
// .asString();
|
||||
|
||||
// 使用我们的封装方法
|
||||
Map<String, Object> result = apiResponseHandler.callGetApi(url, aiApiKey);
|
||||
|
||||
return ResponseEntity.ok(apiResponseHandler.createSuccessResponse(result));
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("调用外部API失败", e);
|
||||
return ResponseEntity.status(500)
|
||||
.body(apiResponseHandler.createErrorResponse("调用外部API失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Token中提取用户名
|
||||
*/
|
||||
private String extractUsernameFromToken(String token) {
|
||||
try {
|
||||
if (token == null || !token.startsWith("Bearer ")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String actualToken = jwtUtils.extractTokenFromHeader(token);
|
||||
if (actualToken == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String username = jwtUtils.getUsernameFromToken(actualToken);
|
||||
if (username != null && !jwtUtils.isTokenExpired(actualToken)) {
|
||||
return username;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.error("解析token失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,54 @@ public class AuthApiController {
|
||||
public ResponseEntity<Map<String, Object>> login(@RequestBody Map<String, String> credentials,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
logger.warn("尝试使用用户名密码登录,但系统已禁用此方式");
|
||||
return ResponseEntity.badRequest()
|
||||
.body(createErrorResponse("系统已禁用用户名密码登录,请使用邮箱验证码登录"));
|
||||
try {
|
||||
// 支持使用邮箱+密码登录(账号就是邮箱)或用户名+密码
|
||||
String emailOrUsername = credentials.get("email");
|
||||
if (emailOrUsername == null || emailOrUsername.trim().isEmpty()) {
|
||||
// 兼容旧客户端使用 username 字段
|
||||
emailOrUsername = credentials.get("username");
|
||||
}
|
||||
String password = credentials.get("password");
|
||||
|
||||
if (emailOrUsername == null || emailOrUsername.trim().isEmpty() || password == null) {
|
||||
return ResponseEntity.badRequest().body(createErrorResponse("邮箱/用户名或密码不能为空"));
|
||||
}
|
||||
|
||||
// 先尝试按邮箱查找用户
|
||||
com.example.demo.model.User user = userService.findByEmailOrNull(emailOrUsername);
|
||||
if (user == null) {
|
||||
// 再按用户名查找
|
||||
user = userService.findByUsernameOrNull(emailOrUsername);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
return ResponseEntity.badRequest().body(createErrorResponse("用户不存在"));
|
||||
}
|
||||
|
||||
// 检查密码
|
||||
if (!userService.checkPassword(password, user.getPasswordHash())) {
|
||||
return ResponseEntity.badRequest().body(createErrorResponse("邮箱/用户名或密码不正确"));
|
||||
}
|
||||
|
||||
// 生成JWT Token(为了兼容系统中其他逻辑,使用 user.getUsername() 作为 token subject)
|
||||
String token = jwtUtils.generateToken(user.getUsername(), user.getRole(), user.getId());
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("success", true);
|
||||
body.put("message", "登录成功");
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
// 不返回密码哈希
|
||||
user.setPasswordHash(null);
|
||||
data.put("user", user);
|
||||
data.put("token", token);
|
||||
body.put("data", data);
|
||||
|
||||
logger.info("邮箱/用户名密码登录成功:{}", emailOrUsername);
|
||||
return ResponseEntity.ok(body);
|
||||
} catch (Exception e) {
|
||||
logger.error("用户名密码登录出错:", e);
|
||||
return ResponseEntity.badRequest().body(createErrorResponse("登录失败:" + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,49 +129,68 @@ public class AuthApiController {
|
||||
logger.info("邮箱验证码登录 - 用户不存在,开始自动注册:{}", email);
|
||||
|
||||
// 从邮箱生成用户名(去掉@符号及后面的部分)
|
||||
String username = email.split("@")[0];
|
||||
logger.info("邮箱验证码登录 - 原始邮箱: '{}', 生成的用户名: '{}'", email, username);
|
||||
String emailPrefix = email.split("@")[0];
|
||||
String emailDomain = email.contains("@") ? email.split("@")[1] : "";
|
||||
logger.info("邮箱验证码登录 - 原始邮箱: '{}', 邮箱前缀: '{}', 邮箱域名: '{}'", email, emailPrefix, emailDomain);
|
||||
|
||||
// 清理用户名(移除特殊字符,只保留字母、数字、下划线)
|
||||
username = username.replaceAll("[^a-zA-Z0-9_]", "_");
|
||||
// 清理邮箱前缀(移除特殊字符,只保留字母、数字、下划线)
|
||||
String cleanPrefix = emailPrefix.replaceAll("[^a-zA-Z0-9_]", "_");
|
||||
|
||||
// 确保用户名长度不超过50个字符
|
||||
if (username.length() > 50) {
|
||||
username = username.substring(0, 50);
|
||||
logger.info("邮箱验证码登录 - 用户名过长,截断为: '{}'", username);
|
||||
}
|
||||
// 生成基础用户名:优先使用邮箱前缀
|
||||
String baseUsername = cleanPrefix;
|
||||
|
||||
// 确保用户名不为空且至少3个字符
|
||||
if (username.length() < 3) {
|
||||
username = username + "_" + System.currentTimeMillis() % 1000;
|
||||
if (username.length() > 50) {
|
||||
username = username.substring(0, 50);
|
||||
}
|
||||
if (baseUsername.length() < 3) {
|
||||
baseUsername = baseUsername + "_" + System.currentTimeMillis() % 1000;
|
||||
}
|
||||
|
||||
// 确保用户名长度不超过50个字符
|
||||
if (baseUsername.length() > 50) {
|
||||
baseUsername = baseUsername.substring(0, 50);
|
||||
logger.info("邮箱验证码登录 - 用户名过长,截断为: '{}'", baseUsername);
|
||||
}
|
||||
|
||||
// 确保用户名唯一
|
||||
String originalUsername = username;
|
||||
// 策略:如果基础用户名已存在,添加邮箱域名的简短标识或数字后缀
|
||||
String username = baseUsername;
|
||||
int counter = 1;
|
||||
while (userService.findByUsernameOrNull(username) != null) {
|
||||
// 如果用户名过长,需要重新截断
|
||||
String baseUsername = originalUsername.length() > 45 ?
|
||||
originalUsername.substring(0, 45) : originalUsername;
|
||||
String newUsername = baseUsername + counter;
|
||||
int maxAttempts = 1000;
|
||||
|
||||
while (userService.findByUsernameOrNull(username) != null && counter <= maxAttempts) {
|
||||
// 尝试策略1:添加邮箱域名的简短标识(如 qq, 163)
|
||||
if (counter == 1 && emailDomain.length() > 0) {
|
||||
String domainShort = emailDomain.split("\\.")[0]; // 获取域名第一部分
|
||||
if (domainShort.length() > 0 && domainShort.length() <= 5) {
|
||||
String candidateUsername = baseUsername.length() > 45 ?
|
||||
baseUsername.substring(0, 45) : baseUsername;
|
||||
candidateUsername = candidateUsername + "_" + domainShort;
|
||||
if (candidateUsername.length() <= 50 &&
|
||||
userService.findByUsernameOrNull(candidateUsername) == null) {
|
||||
username = candidateUsername;
|
||||
logger.info("邮箱验证码登录 - 使用域名标识生成用户名: '{}'", username);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 策略2:添加数字后缀
|
||||
String baseForSuffix = baseUsername.length() > 45 ?
|
||||
baseUsername.substring(0, 45) : baseUsername;
|
||||
String newUsername = baseForSuffix + counter;
|
||||
if (newUsername.length() > 50) {
|
||||
newUsername = newUsername.substring(0, 50);
|
||||
}
|
||||
username = newUsername;
|
||||
counter++;
|
||||
|
||||
// 防止无限循环
|
||||
if (counter > 1000) {
|
||||
// 使用时间戳确保唯一性
|
||||
username = "user_" + System.currentTimeMillis();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("邮箱验证码登录 - 最终用户名: '{}'", username);
|
||||
// 如果还是冲突,使用时间戳确保唯一性
|
||||
if (counter > maxAttempts || userService.findByUsernameOrNull(username) != null) {
|
||||
username = "user_" + System.currentTimeMillis();
|
||||
logger.warn("邮箱验证码登录 - 用户名冲突过多,使用时间戳生成: '{}'", username);
|
||||
}
|
||||
|
||||
logger.info("邮箱验证码登录 - 最终用户名: '{}' (原始邮箱: '{}')", username, email);
|
||||
|
||||
// 直接创建用户对象并设置所有必要字段
|
||||
user = new User();
|
||||
@@ -266,17 +330,50 @@ public class AuthApiController {
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
@GetMapping("/me")
|
||||
public ResponseEntity<Map<String, Object>> getCurrentUser(Authentication authentication) {
|
||||
public ResponseEntity<Map<String, Object>> getCurrentUser(Authentication authentication,
|
||||
jakarta.servlet.http.HttpServletRequest request) {
|
||||
try {
|
||||
logger.info("获取当前用户信息 - authentication: {}", authentication);
|
||||
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
User user = null;
|
||||
String username = authentication.getName();
|
||||
logger.info("当前用户名: {}", username);
|
||||
|
||||
try {
|
||||
User user = userService.findByUsername(username);
|
||||
logger.info("找到用户: {}", user.getUsername());
|
||||
// 首先尝试通过用户名查找
|
||||
user = userService.findByUsernameOrNull(username);
|
||||
|
||||
// 如果通过用户名找不到,尝试从JWT token中获取用户ID
|
||||
if (user == null) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
String token = authHeader.substring(7);
|
||||
try {
|
||||
Long userId = jwtUtils.getUserIdFromToken(token);
|
||||
if (userId != null) {
|
||||
logger.warn("通过用户名未找到用户,尝试通过用户ID查找: {}", userId);
|
||||
user = userService.findById(userId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("从token获取用户ID失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果通过用户名找不到,尝试通过邮箱查找(兼容邮箱登录的情况)
|
||||
if (user == null && username.contains("@")) {
|
||||
logger.warn("通过用户名未找到用户,尝试通过邮箱查找: {}", username);
|
||||
user = userService.findByEmailOrNull(username);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
logger.error("无法找到用户,用户名/邮箱: {}", username);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(createErrorResponse("用户不存在: " + username));
|
||||
}
|
||||
|
||||
logger.info("找到用户: ID={}, 用户名={}, 邮箱={}", user.getId(), user.getUsername(), user.getEmail());
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
@@ -285,7 +382,7 @@ public class AuthApiController {
|
||||
} catch (Exception e) {
|
||||
logger.error("查找用户失败: {}", username, e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(createErrorResponse("用户不存在: " + username));
|
||||
.body(createErrorResponse("查找用户失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,29 +7,94 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
|
||||
/**
|
||||
* 处理 JSON 解析错误
|
||||
* 这个异常通常是因为请求体中的 JSON 格式不正确
|
||||
*/
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleJsonParseException(
|
||||
HttpMessageNotReadableException ex,
|
||||
HttpServletRequest request) {
|
||||
|
||||
logger.error("=== JSON 解析错误 ===");
|
||||
logger.error("请求URL: {}", request.getRequestURI());
|
||||
logger.error("请求方法: {}", request.getMethod());
|
||||
logger.error("Content-Type: {}", request.getContentType());
|
||||
logger.error("错误消息: {}", ex.getMessage());
|
||||
|
||||
// 记录所有请求头
|
||||
logger.error("请求头:");
|
||||
request.getHeaderNames().asIterator().forEachRemaining(headerName -> {
|
||||
logger.error(" {}: {}", headerName, request.getHeader(headerName));
|
||||
});
|
||||
|
||||
// 获取根本原因
|
||||
Throwable rootCause = ex.getRootCause();
|
||||
if (rootCause != null) {
|
||||
logger.error("根本原因: {}", rootCause.getMessage());
|
||||
if (rootCause instanceof JsonParseException) {
|
||||
JsonParseException jsonEx = (JsonParseException) rootCause;
|
||||
logger.error("JSON解析位置: line {}, column {}",
|
||||
jsonEx.getLocation().getLineNr(),
|
||||
jsonEx.getLocation().getColumnNr());
|
||||
}
|
||||
}
|
||||
|
||||
ex.printStackTrace();
|
||||
|
||||
Map<String, Object> errorResponse = new HashMap<>();
|
||||
errorResponse.put("success", false);
|
||||
errorResponse.put("message", "请求数据格式错误:JSON 格式不正确");
|
||||
errorResponse.put("error", "InvalidJsonFormat");
|
||||
errorResponse.put("details", "请检查发送的数据格式,确保是有效的 JSON");
|
||||
errorResponse.put("path", request.getRequestURI());
|
||||
|
||||
if (rootCause != null && rootCause.getMessage() != null) {
|
||||
// 提取有用的错误信息,但不暴露太多内部细节
|
||||
String errorMsg = rootCause.getMessage();
|
||||
if (errorMsg.length() > 200) {
|
||||
errorMsg = errorMsg.substring(0, 200) + "...";
|
||||
}
|
||||
errorResponse.put("parseError", errorMsg);
|
||||
}
|
||||
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理其他所有异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<Map<String, Object>> handleException(Exception e) {
|
||||
logger.error("全局异常处理器捕获到异常", e);
|
||||
public ResponseEntity<Map<String, Object>> handleException(Exception e, HttpServletRequest request) {
|
||||
logger.error("全局异常处理器捕获到异常");
|
||||
logger.error("请求URL: {}", request.getRequestURI());
|
||||
logger.error("请求方法: {}", request.getMethod());
|
||||
logger.error("异常类型: {}", e.getClass().getName());
|
||||
logger.error("异常消息: {}", e.getMessage());
|
||||
if (e.getCause() != null) {
|
||||
logger.error("异常原因: {}", e.getCause().getMessage());
|
||||
}
|
||||
e.printStackTrace();
|
||||
|
||||
|
||||
Map<String, Object> errorResponse = new HashMap<>();
|
||||
errorResponse.put("success", false);
|
||||
errorResponse.put("message", "服务器内部错误: " + e.getMessage());
|
||||
errorResponse.put("error", e.getClass().getSimpleName());
|
||||
|
||||
errorResponse.put("path", request.getRequestURI());
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
|
||||
/**
|
||||
* 健康检查控制器
|
||||
* 用于监控服务状态,无需认证
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/health")
|
||||
@Tag(name = "健康检查", description = "服务健康状态监控接口")
|
||||
public class HealthCheckController {
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* 健康检查接口
|
||||
* @return 服务健康状态
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "健康检查", description = "检查服务运行状态、数据库连接等")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "服务正常"),
|
||||
@ApiResponse(responseCode = "503", description = "服务异常")
|
||||
})
|
||||
public ResponseEntity<Map<String, Object>> healthCheck() {
|
||||
Map<String, Object> health = new HashMap<>();
|
||||
boolean isHealthy = true;
|
||||
|
||||
try {
|
||||
// 应用状态
|
||||
health.put("status", "UP");
|
||||
health.put("timestamp", LocalDateTime.now().toString());
|
||||
health.put("service", "AIGC Platform API");
|
||||
|
||||
// 数据库连接检查
|
||||
Map<String, Object> database = new HashMap<>();
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
boolean isValid = connection.isValid(5); // 5秒超时
|
||||
database.put("status", isValid ? "UP" : "DOWN");
|
||||
database.put("type", "MySQL");
|
||||
if (!isValid) {
|
||||
isHealthy = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
database.put("status", "DOWN");
|
||||
database.put("error", e.getMessage());
|
||||
isHealthy = false;
|
||||
}
|
||||
health.put("database", database);
|
||||
|
||||
// 系统信息
|
||||
Map<String, Object> system = new HashMap<>();
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
system.put("availableProcessors", runtime.availableProcessors());
|
||||
system.put("totalMemory", runtime.totalMemory() / (1024 * 1024) + " MB");
|
||||
system.put("freeMemory", runtime.freeMemory() / (1024 * 1024) + " MB");
|
||||
system.put("maxMemory", runtime.maxMemory() / (1024 * 1024) + " MB");
|
||||
health.put("system", system);
|
||||
|
||||
// 返回状态
|
||||
if (isHealthy) {
|
||||
return ResponseEntity.ok(health);
|
||||
} else {
|
||||
health.put("status", "DOWN");
|
||||
return ResponseEntity.status(503).body(health);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
health.put("status", "DOWN");
|
||||
health.put("error", e.getMessage());
|
||||
return ResponseEntity.status(503).body(health);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的健康检查(轻量级)
|
||||
* @return 简单的健康状态
|
||||
*/
|
||||
@GetMapping("/ping")
|
||||
@Operation(summary = "简单健康检查", description = "快速检查服务是否在线")
|
||||
@ApiResponse(responseCode = "200", description = "服务在线")
|
||||
public ResponseEntity<Map<String, String>> ping() {
|
||||
Map<String, String> response = new HashMap<>();
|
||||
response.put("status", "OK");
|
||||
response.put("message", "pong");
|
||||
response.put("timestamp", LocalDateTime.now().toString());
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 就绪检查(Readiness Probe)
|
||||
* @return 服务是否就绪
|
||||
*/
|
||||
@GetMapping("/ready")
|
||||
@Operation(summary = "就绪检查", description = "检查服务是否准备好处理请求")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "服务就绪"),
|
||||
@ApiResponse(responseCode = "503", description = "服务未就绪")
|
||||
})
|
||||
public ResponseEntity<Map<String, Object>> readiness() {
|
||||
Map<String, Object> readiness = new HashMap<>();
|
||||
boolean isReady = true;
|
||||
|
||||
try {
|
||||
// 检查数据库连接
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
if (!connection.isValid(5)) {
|
||||
isReady = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
isReady = false;
|
||||
readiness.put("error", "Database connection failed: " + e.getMessage());
|
||||
}
|
||||
|
||||
readiness.put("ready", isReady);
|
||||
readiness.put("timestamp", LocalDateTime.now().toString());
|
||||
|
||||
if (isReady) {
|
||||
return ResponseEntity.ok(readiness);
|
||||
} else {
|
||||
return ResponseEntity.status(503).body(readiness);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
readiness.put("ready", false);
|
||||
readiness.put("error", e.getMessage());
|
||||
return ResponseEntity.status(503).body(readiness);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 存活检查(Liveness Probe)
|
||||
* @return 服务是否存活
|
||||
*/
|
||||
@GetMapping("/live")
|
||||
@Operation(summary = "存活检查", description = "检查服务是否存活")
|
||||
@ApiResponse(responseCode = "200", description = "服务存活")
|
||||
public ResponseEntity<Map<String, Object>> liveness() {
|
||||
Map<String, Object> liveness = new HashMap<>();
|
||||
liveness.put("alive", true);
|
||||
liveness.put("timestamp", LocalDateTime.now().toString());
|
||||
return ResponseEntity.ok(liveness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,17 +27,21 @@ import com.example.demo.model.OrderStatus;
|
||||
import com.example.demo.model.PaymentMethod;
|
||||
import com.example.demo.model.User;
|
||||
import com.example.demo.service.OrderService;
|
||||
import com.example.demo.service.UserService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/orders")
|
||||
public class OrderApiController {
|
||||
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OrderApiController.class);
|
||||
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 获取订单列表
|
||||
@@ -59,8 +63,9 @@ public class OrderApiController {
|
||||
response.put("message", "用户未认证,请重新登录");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
if (user == null) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -81,11 +86,49 @@ public class OrderApiController {
|
||||
// 普通用户只能查看自己的订单
|
||||
orderPage = orderService.findOrdersByUser(user, pageable, status, search);
|
||||
}
|
||||
|
||||
|
||||
// 转换订单数据,添加支付方式信息
|
||||
Page<Map<String, Object>> orderDataPage = orderPage.map(order -> {
|
||||
Map<String, Object> orderData = new HashMap<>();
|
||||
orderData.put("id", order.getId());
|
||||
orderData.put("orderNumber", order.getOrderNumber());
|
||||
orderData.put("totalAmount", order.getTotalAmount());
|
||||
orderData.put("currency", order.getCurrency());
|
||||
orderData.put("status", order.getStatus());
|
||||
orderData.put("orderType", order.getOrderType());
|
||||
orderData.put("description", order.getDescription());
|
||||
orderData.put("createdAt", order.getCreatedAt());
|
||||
orderData.put("updatedAt", order.getUpdatedAt());
|
||||
orderData.put("paidAt", order.getPaidAt());
|
||||
|
||||
// 添加用户信息
|
||||
if (order.getUser() != null) {
|
||||
Map<String, Object> userData = new HashMap<>();
|
||||
userData.put("id", order.getUser().getId());
|
||||
userData.put("username", order.getUser().getUsername());
|
||||
orderData.put("user", userData);
|
||||
}
|
||||
|
||||
// 从订单关联的Payment中获取支付方式
|
||||
String paymentMethod = null;
|
||||
if (order.getPayments() != null && !order.getPayments().isEmpty()) {
|
||||
// 获取最新的支付记录
|
||||
var latestPayment = order.getPayments().stream()
|
||||
.filter(p -> p.getPaymentMethod() != null)
|
||||
.findFirst();
|
||||
if (latestPayment.isPresent()) {
|
||||
paymentMethod = latestPayment.get().getPaymentMethod().name();
|
||||
}
|
||||
}
|
||||
orderData.put("paymentMethod", paymentMethod);
|
||||
|
||||
return orderData;
|
||||
});
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("data", orderPage);
|
||||
|
||||
response.put("data", orderDataPage);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -109,15 +152,16 @@ public class OrderApiController {
|
||||
response.put("message", "用户未认证,请重新登录");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
if (user == null) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "用户信息获取失败,请重新登录");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
|
||||
Order order = orderService.findById(id)
|
||||
.orElse(null);
|
||||
|
||||
@@ -165,15 +209,16 @@ public class OrderApiController {
|
||||
response.put("message", "用户未认证,请重新登录");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
if (user == null) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "用户信息获取失败,请重新登录");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
|
||||
order.setUser(user);
|
||||
|
||||
Order createdOrder = orderService.createOrder(order);
|
||||
@@ -200,7 +245,8 @@ public class OrderApiController {
|
||||
@RequestBody Map<String, String> request,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Order order = orderService.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("订单不存在"));
|
||||
|
||||
@@ -242,7 +288,8 @@ public class OrderApiController {
|
||||
@RequestBody Map<String, String> request,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Order order = orderService.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("订单不存在"));
|
||||
|
||||
@@ -277,8 +324,9 @@ public class OrderApiController {
|
||||
@RequestBody Map<String, String> request,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
|
||||
// 只有管理员可以发货
|
||||
if (!user.getRole().equals("ROLE_ADMIN")) {
|
||||
return ResponseEntity.badRequest()
|
||||
@@ -309,8 +357,9 @@ public class OrderApiController {
|
||||
public ResponseEntity<Map<String, Object>> completeOrder(@PathVariable Long id,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
|
||||
// 只有管理员可以完成订单
|
||||
if (!user.getRole().equals("ROLE_ADMIN")) {
|
||||
return ResponseEntity.badRequest()
|
||||
@@ -341,7 +390,8 @@ public class OrderApiController {
|
||||
@RequestBody Map<String, String> request,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Order order = orderService.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("订单不存在"));
|
||||
|
||||
@@ -392,8 +442,9 @@ public class OrderApiController {
|
||||
response.put("message", "用户未认证,请重新登录");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
if (user == null) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -431,7 +482,8 @@ public class OrderApiController {
|
||||
public ResponseEntity<Map<String, Object>> deleteOrder(@PathVariable Long id,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Order order = orderService.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("订单不存在"));
|
||||
|
||||
@@ -463,8 +515,9 @@ public class OrderApiController {
|
||||
public ResponseEntity<Map<String, Object>> deleteOrders(@RequestBody List<Long> orderIds,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
|
||||
// 只有管理员可以批量删除
|
||||
if (!user.getRole().equals("ROLE_ADMIN")) {
|
||||
return ResponseEntity.badRequest()
|
||||
|
||||
@@ -31,20 +31,24 @@ import com.example.demo.model.PaymentMethod;
|
||||
import com.example.demo.model.User;
|
||||
import com.example.demo.service.OrderService;
|
||||
import com.example.demo.service.PaymentService;
|
||||
import com.example.demo.service.UserService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/orders")
|
||||
public class OrderController {
|
||||
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
|
||||
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private PaymentService paymentService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 显示订单列表
|
||||
@@ -59,8 +63,9 @@ public class OrderController {
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
Sort sort = sortDir.equalsIgnoreCase("desc") ?
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Sort sort = sortDir.equalsIgnoreCase("desc") ?
|
||||
Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
|
||||
Pageable pageable = PageRequest.of(page, size, sort);
|
||||
|
||||
@@ -94,11 +99,12 @@ public class OrderController {
|
||||
* 显示订单详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public String orderDetail(@PathVariable Long id,
|
||||
Authentication authentication,
|
||||
public String orderDetail(@PathVariable Long id,
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Optional<Order> orderOpt = orderService.findById(id);
|
||||
|
||||
if (!orderOpt.isPresent()) {
|
||||
@@ -152,7 +158,7 @@ public class OrderController {
|
||||
* 处理创建订单
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public String createOrder(@Valid @ModelAttribute Order order,
|
||||
public String createOrder(@Valid @ModelAttribute Order order,
|
||||
BindingResult result,
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
@@ -162,8 +168,9 @@ public class OrderController {
|
||||
model.addAttribute("orderStatuses", OrderStatus.values());
|
||||
return "orders/form";
|
||||
}
|
||||
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
order.setUser(user);
|
||||
|
||||
// 验证订单项
|
||||
@@ -211,7 +218,8 @@ public class OrderController {
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Optional<Order> orderOpt = orderService.findById(id);
|
||||
|
||||
if (!orderOpt.isPresent()) {
|
||||
@@ -253,7 +261,8 @@ public class OrderController {
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Optional<Order> orderOpt = orderService.findById(id);
|
||||
|
||||
if (!orderOpt.isPresent()) {
|
||||
@@ -290,8 +299,9 @@ public class OrderController {
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
|
||||
// 只有管理员可以发货
|
||||
if (!user.getRole().equals("ROLE_ADMIN")) {
|
||||
model.addAttribute("error", "无权限操作此订单");
|
||||
@@ -318,8 +328,9 @@ public class OrderController {
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
|
||||
// 只有管理员可以完成订单
|
||||
if (!user.getRole().equals("ROLE_ADMIN")) {
|
||||
model.addAttribute("error", "无权限操作此订单");
|
||||
@@ -347,7 +358,8 @@ public class OrderController {
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Optional<Order> orderOpt = orderService.findById(id);
|
||||
|
||||
if (!orderOpt.isPresent()) {
|
||||
@@ -400,8 +412,9 @@ public class OrderController {
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
|
||||
// 只有管理员可以访问
|
||||
if (!user.getRole().equals("ROLE_ADMIN")) {
|
||||
model.addAttribute("error", "无权限访问");
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.example.demo.repository.PaymentRepository;
|
||||
import com.example.demo.service.AlipayService;
|
||||
import com.example.demo.service.PaymentService;
|
||||
import com.example.demo.service.UserService;
|
||||
import com.example.demo.util.JwtUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/payments")
|
||||
@@ -46,6 +47,9 @@ public class PaymentApiController {
|
||||
|
||||
@Autowired
|
||||
private PaymentRepository paymentRepository;
|
||||
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -377,7 +381,8 @@ public class PaymentApiController {
|
||||
*/
|
||||
@GetMapping("/subscription/info")
|
||||
public ResponseEntity<Map<String, Object>> getUserSubscriptionInfo(
|
||||
Authentication authentication) {
|
||||
Authentication authentication,
|
||||
jakarta.servlet.http.HttpServletRequest request) {
|
||||
try {
|
||||
logger.info("=== 开始获取用户订阅信息 ===");
|
||||
|
||||
@@ -387,10 +392,40 @@ public class PaymentApiController {
|
||||
.body(createErrorResponse("请先登录"));
|
||||
}
|
||||
|
||||
User user = null;
|
||||
String username = authentication.getName();
|
||||
logger.info("认证用户名: {}", username);
|
||||
|
||||
try {
|
||||
// Principal 现在是用户名字符串,直接通过用户名查找
|
||||
user = userService.findByUsernameOrNull(username);
|
||||
|
||||
// 如果通过用户名找不到,尝试从JWT token中获取用户ID
|
||||
if (user == null) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
String token = authHeader.substring(7);
|
||||
try {
|
||||
Long userId = jwtUtils.getUserIdFromToken(token);
|
||||
if (userId != null) {
|
||||
logger.warn("通过用户名未找到用户,尝试通过用户ID查找: {}", userId);
|
||||
user = userService.findById(userId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("从token获取用户ID失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果通过用户名找不到,尝试通过邮箱查找(兼容邮箱登录的情况)
|
||||
if (user == null && username.contains("@")) {
|
||||
logger.warn("通过用户名未找到用户,尝试通过邮箱查找: {}", username);
|
||||
user = userService.findByEmailOrNull(username);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("查找用户失败: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
User user = userService.findByUsername(username);
|
||||
logger.info("查找用户结果: {}", user != null ? "找到用户,ID: " + user.getId() : "未找到用户");
|
||||
|
||||
if (user == null) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.example.demo.model.PaymentMethod;
|
||||
import com.example.demo.model.User;
|
||||
import com.example.demo.service.AlipayService;
|
||||
import com.example.demo.service.PaymentService;
|
||||
import com.example.demo.service.UserService;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
@@ -29,14 +30,17 @@ import jakarta.validation.Valid;
|
||||
@Controller
|
||||
@RequestMapping("/payment")
|
||||
public class PaymentController {
|
||||
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PaymentController.class);
|
||||
|
||||
|
||||
@Autowired
|
||||
private PaymentService paymentService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private AlipayService alipayService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 显示支付页面
|
||||
@@ -64,7 +68,8 @@ public class PaymentController {
|
||||
|
||||
try {
|
||||
// 设置当前用户
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
payment.setUser(user);
|
||||
|
||||
// 设置默认货币
|
||||
@@ -208,7 +213,8 @@ public class PaymentController {
|
||||
@GetMapping("/history")
|
||||
public String paymentHistory(Authentication authentication, Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
List<Payment> payments = paymentService.findByUserId(user.getId());
|
||||
model.addAttribute("payments", payments);
|
||||
return "payment/history";
|
||||
@@ -223,11 +229,12 @@ public class PaymentController {
|
||||
* 支付详情
|
||||
*/
|
||||
@GetMapping("/detail/{id}")
|
||||
public String paymentDetail(@PathVariable Long id,
|
||||
Authentication authentication,
|
||||
public String paymentDetail(@PathVariable Long id,
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
try {
|
||||
User user = (User) authentication.getPrincipal();
|
||||
String username = authentication.getName();
|
||||
User user = userService.findByUsername(username);
|
||||
Payment payment = paymentService.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("支付记录不存在"));
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.example.demo.service.PollingQueryService;
|
||||
|
||||
/**
|
||||
* 轮询查询测试控制器
|
||||
* 用于测试和监控轮询查询功能
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/polling")
|
||||
public class PollingTestController {
|
||||
|
||||
@Autowired
|
||||
private PollingQueryService pollingQueryService;
|
||||
|
||||
|
||||
/**
|
||||
* 获取轮询查询统计信息
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public ResponseEntity<Map<String, Object>> getPollingStats() {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
try {
|
||||
String stats = pollingQueryService.getPollingStats();
|
||||
response.put("success", true);
|
||||
response.put("message", "轮询查询统计信息");
|
||||
response.put("stats", stats);
|
||||
response.put("timestamp", System.currentTimeMillis());
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
response.put("success", false);
|
||||
response.put("message", "获取统计信息失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发轮询查询
|
||||
*/
|
||||
@PostMapping("/trigger")
|
||||
public ResponseEntity<Map<String, Object>> triggerPolling() {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
try {
|
||||
pollingQueryService.manualPollingQuery();
|
||||
response.put("success", true);
|
||||
response.put("message", "手动触发轮询查询成功");
|
||||
response.put("timestamp", System.currentTimeMillis());
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
response.put("success", false);
|
||||
response.put("message", "手动触发轮询查询失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查轮询查询配置
|
||||
*/
|
||||
@GetMapping("/config")
|
||||
public ResponseEntity<Map<String, Object>> getPollingConfig() {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
try {
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("pollingInterval", "120000ms (2分钟)");
|
||||
config.put("scheduledMethod", "TaskStatusPollingService.pollTaskStatuses()");
|
||||
config.put("scheduledMethod2", "TaskQueueScheduler.checkTaskStatuses()");
|
||||
config.put("scheduledMethod3", "PollingQueryService.executePollingQuery()");
|
||||
config.put("enabled", true);
|
||||
|
||||
response.put("success", true);
|
||||
response.put("message", "轮询查询配置信息");
|
||||
response.put("config", config);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
response.put("success", false);
|
||||
response.put("message", "获取配置信息失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public class PromptOptimizerApiController {
|
||||
@RequestBody Map<String, Object> request,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
String username = authentication.getName();
|
||||
String username = (authentication != null) ? authentication.getName() : "anonymous";
|
||||
logger.info("收到优化提示词请求,用户: {}", username);
|
||||
|
||||
// 从请求中提取参数
|
||||
|
||||
@@ -4,7 +4,13 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -12,18 +18,29 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.example.demo.model.TaskStatus;
|
||||
import com.example.demo.repository.TaskStatusRepository;
|
||||
import com.example.demo.service.TaskStatusPollingService;
|
||||
import com.example.demo.util.JwtUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/task-status")
|
||||
@CrossOrigin(origins = "http://localhost:5173")
|
||||
public class TaskStatusApiController {
|
||||
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TaskStatusApiController.class);
|
||||
|
||||
@Autowired
|
||||
private TaskStatusPollingService taskStatusPollingService;
|
||||
|
||||
@Autowired
|
||||
private TaskStatusRepository taskStatusRepository;
|
||||
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
/**
|
||||
* 获取任务状态
|
||||
@@ -130,44 +147,172 @@ public class TaskStatusApiController {
|
||||
@PostMapping("/poll")
|
||||
public ResponseEntity<Map<String, Object>> triggerPolling(
|
||||
@RequestHeader("Authorization") String token) {
|
||||
|
||||
|
||||
try {
|
||||
// 验证token但不使用用户名(管理员接口)
|
||||
extractUsernameFromToken(token);
|
||||
|
||||
|
||||
// 这里可以添加管理员权限检查
|
||||
// if (!isAdmin(username)) {
|
||||
// return ResponseEntity.status(403).body(Map.of("error", "权限不足"));
|
||||
// }
|
||||
|
||||
|
||||
taskStatusPollingService.pollTaskStatuses();
|
||||
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "轮询已触发");
|
||||
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
Map<String, Object> errorResponse = new HashMap<>();
|
||||
errorResponse.put("error", "触发轮询失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(errorResponse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从token中提取用户名(简化实现)
|
||||
* 获取所有任务记录(管理员功能)
|
||||
*/
|
||||
@GetMapping("/admin/all")
|
||||
public ResponseEntity<Map<String, Object>> getAllTaskRecords(
|
||||
@RequestHeader("Authorization") String token,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String taskType,
|
||||
@RequestParam(required = false) String search) {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 验证token
|
||||
String username = extractUsernameFromToken(token);
|
||||
if (username == null) {
|
||||
response.put("success", false);
|
||||
response.put("message", "未授权访问");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
// TODO: 添加管理员权限检查
|
||||
// if (!isAdmin(username)) {
|
||||
// response.put("success", false);
|
||||
// response.put("message", "权限不足");
|
||||
// return ResponseEntity.status(403).body(response);
|
||||
// }
|
||||
|
||||
// 输入验证
|
||||
if (page < 0) page = 0;
|
||||
if (size <= 0 || size > 100) size = 10;
|
||||
|
||||
// 创建分页对象
|
||||
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
|
||||
Page<TaskStatus> taskPage;
|
||||
|
||||
// 根据条件查询
|
||||
if (status != null && !status.isEmpty() && !"all".equals(status)) {
|
||||
try {
|
||||
TaskStatus.Status statusEnum = TaskStatus.Status.valueOf(status.toUpperCase());
|
||||
taskPage = taskStatusRepository.findByStatus(statusEnum, pageable);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("无效的任务状态: {}", status);
|
||||
taskPage = taskStatusRepository.findAllWithPagination(pageable);
|
||||
}
|
||||
} else if (taskType != null && !taskType.isEmpty()) {
|
||||
try {
|
||||
TaskStatus.TaskType taskTypeEnum = TaskStatus.TaskType.valueOf(taskType.toUpperCase());
|
||||
taskPage = taskStatusRepository.findByTaskType(taskTypeEnum, pageable);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("无效的任务类型: {}", taskType);
|
||||
taskPage = taskStatusRepository.findAllWithPagination(pageable);
|
||||
}
|
||||
} else {
|
||||
taskPage = taskStatusRepository.findAllWithPagination(pageable);
|
||||
}
|
||||
|
||||
// 构建响应数据
|
||||
List<Map<String, Object>> taskRecords = taskPage.getContent().stream()
|
||||
.map(task -> {
|
||||
Map<String, Object> record = new HashMap<>();
|
||||
record.put("id", task.getId());
|
||||
record.put("taskId", task.getTaskId());
|
||||
record.put("username", task.getUsername());
|
||||
record.put("type", task.getTaskType() != null ? task.getTaskType().getDescription() : "未知");
|
||||
record.put("taskType", task.getTaskType() != null ? task.getTaskType().name() : null);
|
||||
record.put("status", task.getStatus().name());
|
||||
record.put("statusText", task.getStatus().getDescription());
|
||||
record.put("progress", task.getProgress());
|
||||
record.put("resultUrl", task.getResultUrl());
|
||||
record.put("errorMessage", task.getErrorMessage());
|
||||
record.put("createdAt", task.getCreatedAt());
|
||||
record.put("updatedAt", task.getUpdatedAt());
|
||||
record.put("completedAt", task.getCompletedAt());
|
||||
|
||||
// 计算消耗资源(根据任务类型)
|
||||
String resources = "0积分";
|
||||
if (task.getTaskType() != null) {
|
||||
switch (task.getTaskType()) {
|
||||
case TEXT_TO_VIDEO:
|
||||
resources = "10积分";
|
||||
break;
|
||||
case IMAGE_TO_VIDEO:
|
||||
resources = "5积分";
|
||||
break;
|
||||
case STORYBOARD_VIDEO:
|
||||
resources = "15积分";
|
||||
break;
|
||||
}
|
||||
}
|
||||
record.put("resources", resources);
|
||||
|
||||
return record;
|
||||
})
|
||||
.toList();
|
||||
|
||||
response.put("success", true);
|
||||
response.put("data", taskRecords);
|
||||
response.put("totalElements", taskPage.getTotalElements());
|
||||
response.put("totalPages", taskPage.getTotalPages());
|
||||
response.put("currentPage", page);
|
||||
response.put("size", size);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取任务记录失败", e);
|
||||
response.put("success", false);
|
||||
response.put("message", "获取任务记录失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从token中提取用户名
|
||||
*/
|
||||
private String extractUsernameFromToken(String token) {
|
||||
// 这里应该解析JWT token,现在简化处理
|
||||
// 实际实现应该使用JWT工具类
|
||||
String cleanToken = token;
|
||||
if (token.startsWith("Bearer ")) {
|
||||
cleanToken = token.substring(7);
|
||||
try {
|
||||
if (token == null || !token.startsWith("Bearer ")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 提取实际的token
|
||||
String actualToken = jwtUtils.extractTokenFromHeader(token);
|
||||
if (actualToken == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 验证token并获取用户名
|
||||
String username = jwtUtils.getUsernameFromToken(actualToken);
|
||||
if (username != null && !jwtUtils.isTokenExpired(actualToken)) {
|
||||
return username;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.error("解析token失败", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 简化处理,实际应该解析JWT
|
||||
return "admin"; // 临时返回admin,实际应该从JWT中解析
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.example.demo.util.JwtUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/test")
|
||||
public class TestController {
|
||||
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
@GetMapping("/generate-token")
|
||||
public ResponseEntity<Map<String, Object>> generateToken() {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 为admin用户生成新的token
|
||||
String token = jwtUtils.generateToken("admin", "ROLE_ADMIN", 231L);
|
||||
|
||||
response.put("success", true);
|
||||
response.put("token", token);
|
||||
response.put("message", "Token生成成功");
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
response.put("success", false);
|
||||
response.put("message", "Token生成失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/test-auth")
|
||||
public ResponseEntity<Map<String, Object>> testAuth() {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "认证测试成功");
|
||||
response.put("timestamp", System.currentTimeMillis());
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,11 +91,6 @@ public class TextToVideoApiController {
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
|
||||
if (prompt.trim().length() > 1000) {
|
||||
response.put("success", false);
|
||||
response.put("message", "文本描述不能超过1000个字符");
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
|
||||
if (duration < 1 || duration > 60) {
|
||||
response.put("success", false);
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import com.example.demo.model.UserWork;
|
||||
import com.example.demo.service.UserWorkService;
|
||||
import com.example.demo.util.JwtUtils;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.example.demo.model.UserWork;
|
||||
import com.example.demo.service.UserWorkService;
|
||||
import com.example.demo.util.JwtUtils;
|
||||
|
||||
/**
|
||||
* 用户作品API控制器
|
||||
@@ -28,6 +39,38 @@ public class UserWorkApiController {
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
/**
|
||||
* 获取正在进行中的作品
|
||||
*/
|
||||
@GetMapping("/processing")
|
||||
public ResponseEntity<Map<String, Object>> getProcessingWorks(
|
||||
@RequestHeader("Authorization") String token) {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
try {
|
||||
String username = extractUsernameFromToken(token);
|
||||
if (username == null) {
|
||||
response.put("success", false);
|
||||
response.put("message", "用户未登录");
|
||||
return ResponseEntity.status(401).body(response);
|
||||
}
|
||||
|
||||
List<UserWork> processingWorks = userWorkService.getProcessingWorks(username);
|
||||
|
||||
response.put("success", true);
|
||||
response.put("data", processingWorks);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取正在进行中的作品失败", e);
|
||||
response.put("success", false);
|
||||
response.put("message", "获取作品失败:" + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的作品列表
|
||||
*/
|
||||
@@ -75,7 +118,7 @@ public class UserWorkApiController {
|
||||
/**
|
||||
* 获取作品详情
|
||||
*/
|
||||
@GetMapping("/{workId}")
|
||||
@GetMapping("/{workId:\\d+}")
|
||||
public ResponseEntity<Map<String, Object>> getWorkDetail(
|
||||
@PathVariable Long workId,
|
||||
@RequestHeader("Authorization") String token) {
|
||||
@@ -111,7 +154,7 @@ public class UserWorkApiController {
|
||||
/**
|
||||
* 更新作品信息
|
||||
*/
|
||||
@PutMapping("/{workId}")
|
||||
@PutMapping("/{workId:\\d+}")
|
||||
public ResponseEntity<Map<String, Object>> updateWork(
|
||||
@PathVariable Long workId,
|
||||
@RequestHeader("Authorization") String token,
|
||||
@@ -157,7 +200,7 @@ public class UserWorkApiController {
|
||||
/**
|
||||
* 删除作品
|
||||
*/
|
||||
@DeleteMapping("/{workId}")
|
||||
@DeleteMapping("/{workId:\\d+}")
|
||||
public ResponseEntity<Map<String, Object>> deleteWork(
|
||||
@PathVariable Long workId,
|
||||
@RequestHeader("Authorization") String token) {
|
||||
@@ -194,7 +237,7 @@ public class UserWorkApiController {
|
||||
/**
|
||||
* 点赞作品
|
||||
*/
|
||||
@PostMapping("/{workId}/like")
|
||||
@PostMapping("/{workId:\\d+}/like")
|
||||
public ResponseEntity<Map<String, Object>> likeWork(
|
||||
@PathVariable Long workId,
|
||||
@RequestHeader("Authorization") String token) {
|
||||
@@ -234,7 +277,7 @@ public class UserWorkApiController {
|
||||
/**
|
||||
* 下载作品
|
||||
*/
|
||||
@PostMapping("/{workId}/download")
|
||||
@PostMapping("/{workId:\\d+}/download")
|
||||
public ResponseEntity<Map<String, Object>> downloadWork(
|
||||
@PathVariable Long workId,
|
||||
@RequestHeader("Authorization") String token) {
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import com.example.demo.service.VerificationCodeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.example.demo.service.VerificationCodeService;
|
||||
|
||||
/**
|
||||
* 验证码控制器
|
||||
*/
|
||||
@@ -15,6 +21,8 @@ import java.util.Map;
|
||||
@RequestMapping("/api/verification")
|
||||
public class VerificationCodeController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(VerificationCodeController.class);
|
||||
|
||||
@Autowired
|
||||
private VerificationCodeService verificationCodeService;
|
||||
|
||||
@@ -24,10 +32,12 @@ public class VerificationCodeController {
|
||||
*/
|
||||
@PostMapping("/email/send")
|
||||
public ResponseEntity<Map<String, Object>> sendEmailCode(@RequestBody Map<String, String> request) {
|
||||
logger.info("收到发送验证码请求,参数: {}", request);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
String email = request.get("email");
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
logger.warn("验证码发送请求失败:邮箱为空");
|
||||
response.put("success", false);
|
||||
response.put("message", "邮箱不能为空");
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
@@ -35,25 +45,31 @@ public class VerificationCodeController {
|
||||
|
||||
// 简单的邮箱格式验证
|
||||
if (!email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
|
||||
logger.warn("验证码发送请求失败:邮箱格式不正确,邮箱: {}", email);
|
||||
response.put("success", false);
|
||||
response.put("message", "邮箱格式不正确");
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
|
||||
logger.info("开始发送验证码到邮箱: {}", email);
|
||||
try {
|
||||
boolean success = verificationCodeService.sendEmailVerificationCode(email);
|
||||
if (success) {
|
||||
logger.info("验证码发送成功,邮箱: {}", email);
|
||||
response.put("success", true);
|
||||
response.put("message", "验证码发送成功");
|
||||
} else {
|
||||
logger.warn("验证码发送失败,邮箱: {}", email);
|
||||
response.put("success", false);
|
||||
response.put("message", "验证码发送失败,请稍后重试");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("验证码发送异常,邮箱: {}", email, e);
|
||||
response.put("success", false);
|
||||
response.put("message", "验证码发送失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
logger.info("验证码发送请求处理完成,邮箱: {}, 结果: {}", email, response.get("success"));
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,3 +76,7 @@ public class MailMessage {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -269,4 +269,8 @@ public class TaskStatus {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -100,7 +100,8 @@ public class UserWork {
|
||||
public enum WorkType {
|
||||
TEXT_TO_VIDEO("文生视频"),
|
||||
IMAGE_TO_VIDEO("图生视频"),
|
||||
STORYBOARD_VIDEO("分镜视频");
|
||||
STORYBOARD_VIDEO("分镜视频"),
|
||||
STORYBOARD_IMAGE("分镜图");
|
||||
|
||||
private final String description;
|
||||
|
||||
|
||||
@@ -58,6 +58,13 @@ public interface TaskQueueRepository extends JpaRepository<TaskQueue, Long> {
|
||||
*/
|
||||
@Query("SELECT tq FROM TaskQueue tq WHERE tq.status = 'PROCESSING' AND tq.checkCount < tq.maxCheckCount ORDER BY tq.lastCheckTime ASC NULLS FIRST, tq.createdAt ASC")
|
||||
List<TaskQueue> findTasksToCheck();
|
||||
|
||||
/**
|
||||
* 统计需要检查的任务数量(状态为PROCESSING且未超时)
|
||||
* 用于快速检查是否有待处理任务
|
||||
*/
|
||||
@Query("SELECT COUNT(tq) FROM TaskQueue tq WHERE tq.status = 'PROCESSING' AND tq.checkCount < tq.maxCheckCount")
|
||||
long countTasksToCheck();
|
||||
|
||||
/**
|
||||
* 查找超时的任务
|
||||
|
||||
@@ -33,6 +33,12 @@ public interface TaskStatusRepository extends JpaRepository<TaskStatus, Long> {
|
||||
*/
|
||||
@Query("SELECT t FROM TaskStatus t WHERE t.status = 'PROCESSING' AND t.pollCount < t.maxPolls AND (t.lastPolledAt IS NULL OR t.lastPolledAt < :cutoffTime)")
|
||||
List<TaskStatus> findTasksNeedingPolling(@Param("cutoffTime") LocalDateTime cutoffTime);
|
||||
|
||||
/**
|
||||
* 计数:查找需要轮询的任务数量(用于快速短路判断,避免加载实体列表)
|
||||
*/
|
||||
@Query("SELECT COUNT(t) FROM TaskStatus t WHERE t.status = 'PROCESSING' AND t.pollCount < t.maxPolls AND (t.lastPolledAt IS NULL OR t.lastPolledAt < :cutoffTime)")
|
||||
long countTasksNeedingPolling(@Param("cutoffTime") LocalDateTime cutoffTime);
|
||||
|
||||
/**
|
||||
* 查找超时的任务
|
||||
@@ -49,6 +55,11 @@ public interface TaskStatusRepository extends JpaRepository<TaskStatus, Long> {
|
||||
* 统计用户的任务数量
|
||||
*/
|
||||
long countByUsername(String username);
|
||||
|
||||
/**
|
||||
* 根据状态统计任务数量(用于快速短路判断)
|
||||
*/
|
||||
long countByStatus(TaskStatus.Status status);
|
||||
|
||||
/**
|
||||
* 统计用户指定状态的任务数量
|
||||
@@ -60,6 +71,22 @@ public interface TaskStatusRepository extends JpaRepository<TaskStatus, Long> {
|
||||
*/
|
||||
@Query("SELECT t FROM TaskStatus t WHERE t.username = :username ORDER BY t.createdAt DESC")
|
||||
List<TaskStatus> findRecentTasksByUsername(@Param("username") String username, org.springframework.data.domain.Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查找所有任务(管理员功能,支持分页)
|
||||
*/
|
||||
@Query("SELECT t FROM TaskStatus t ORDER BY t.createdAt DESC")
|
||||
org.springframework.data.domain.Page<TaskStatus> findAllWithPagination(org.springframework.data.domain.Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据状态查找任务(管理员功能,支持分页)
|
||||
*/
|
||||
org.springframework.data.domain.Page<TaskStatus> findByStatus(TaskStatus.Status status, org.springframework.data.domain.Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据任务类型查找任务(管理员功能,支持分页)
|
||||
*/
|
||||
org.springframework.data.domain.Page<TaskStatus> findByTaskType(TaskStatus.TaskType taskType, org.springframework.data.domain.Pageable pageable);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,3 +105,4 @@ public interface TaskStatusRepository extends JpaRepository<TaskStatus, Long> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@ public interface UserWorkRepository extends JpaRepository<UserWork, Long> {
|
||||
@Query("SELECT uw FROM UserWork uw WHERE uw.username = :username AND uw.status = :status ORDER BY uw.createdAt DESC")
|
||||
List<UserWork> findByUsernameAndStatusOrderByCreatedAtDesc(@Param("username") String username, @Param("status") UserWork.WorkStatus status);
|
||||
|
||||
/**
|
||||
* 根据用户名查找正在进行中和排队中的作品
|
||||
*/
|
||||
@Query("SELECT uw FROM UserWork uw WHERE uw.username = :username AND (uw.status = 'PROCESSING' OR uw.status = 'PENDING') ORDER BY uw.createdAt DESC")
|
||||
List<UserWork> findByUsernameAndProcessingOrPendingOrderByCreatedAtDesc(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 根据任务ID查找作品
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,12 @@ public class TaskQueueScheduler {
|
||||
@Autowired
|
||||
private TaskQueueService taskQueueService;
|
||||
|
||||
@Autowired
|
||||
private com.example.demo.repository.TaskStatusRepository taskStatusRepository;
|
||||
|
||||
@Autowired
|
||||
private com.example.demo.service.TaskStatusPollingService taskStatusPollingService;
|
||||
|
||||
@Autowired
|
||||
private TaskCleanupService taskCleanupService;
|
||||
|
||||
@@ -56,11 +62,28 @@ public class TaskQueueScheduler {
|
||||
* 检查任务状态 - 每2分钟执行一次轮询查询
|
||||
* 固定间隔:120000毫秒 = 2分钟
|
||||
* 查询正在处理的任务状态,更新完成/失败状态
|
||||
* 优化:只在有待处理任务时才执行轮询,节省资源
|
||||
*/
|
||||
@Scheduled(fixedRate = 120000) // 2分钟 = 120000毫秒
|
||||
public void checkTaskStatuses() {
|
||||
try {
|
||||
// 新策略:仅在任务队列中存在待处理任务时才进行轮询查询
|
||||
boolean hasQueueTasks = taskQueueService.hasTasksToCheck();
|
||||
if (!hasQueueTasks) {
|
||||
// 没有待处理任务,静默跳过轮询,不输出日志以减少噪音
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("发现待处理任务,开始轮询查询");
|
||||
|
||||
// 队列中有任务:检查队列内任务状态,并在必要时调用状态轮询(如果存在正在PROCESSING的任务)
|
||||
taskQueueService.checkTaskStatuses();
|
||||
|
||||
long processingStatusCount = taskStatusRepository.countByStatus(com.example.demo.model.TaskStatus.Status.PROCESSING);
|
||||
if (processingStatusCount > 0) {
|
||||
// TaskStatusPollingService 的 @Scheduled 已被禁用,统一由此处调用
|
||||
taskStatusPollingService.pollTaskStatuses();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("检查任务状态失败", e);
|
||||
}
|
||||
|
||||
@@ -38,35 +38,68 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
||||
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
||||
@NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
logger.info("JWT过滤器处理请求: {}", request.getRequestURI());
|
||||
|
||||
|
||||
String requestURI = request.getRequestURI();
|
||||
logger.info("JWT过滤器处理请求: {}", requestURI);
|
||||
|
||||
// 定义不需要JWT验证的路径
|
||||
String[] publicPaths = {
|
||||
"/api/auth/login",
|
||||
"/api/auth/login/email",
|
||||
"/api/auth/register",
|
||||
"/api/verification/",
|
||||
"/api/public/",
|
||||
"/api/email/",
|
||||
"/api/payments/alipay/notify",
|
||||
"/api/payments/alipay/return",
|
||||
"/swagger-ui",
|
||||
"/v3/api-docs",
|
||||
"/api-docs"
|
||||
};
|
||||
|
||||
// 检查是否是公开路径
|
||||
boolean isPublicPath = false;
|
||||
for (String publicPath : publicPaths) {
|
||||
if (requestURI.contains(publicPath)) {
|
||||
isPublicPath = true;
|
||||
logger.info("JWT过滤器: 跳过公开路径 {}", requestURI);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是公开路径,直接放行,不进行JWT验证
|
||||
if (isPublicPath) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
logger.debug("Authorization头: {}", authHeader);
|
||||
|
||||
|
||||
String token = jwtUtils.extractTokenFromHeader(authHeader);
|
||||
logger.debug("提取的token: {}", token != null ? "存在" : "不存在");
|
||||
|
||||
if (token != null && !token.equals("null") && !token.trim().isEmpty()) {
|
||||
String username = jwtUtils.getUsernameFromToken(token);
|
||||
logger.debug("从token获取用户名: {}", username);
|
||||
|
||||
|
||||
if (username != null && jwtUtils.validateToken(token, username)) {
|
||||
logger.info("JWT认证 - 从token获取的用户名: '{}'", username);
|
||||
User user = userService.findByUsername(username);
|
||||
logger.info("JWT认证 - 数据库查找结果: {}", user != null ? "找到用户 '" + user.getUsername() + "'" : "未找到用户");
|
||||
|
||||
|
||||
if (user != null) {
|
||||
// 创建用户权限列表
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
authorities.add(new SimpleGrantedAuthority(user.getRole()));
|
||||
|
||||
// 将User对象作为Principal,而不是用户名字符串
|
||||
UsernamePasswordAuthenticationToken authToken =
|
||||
new UsernamePasswordAuthenticationToken(user, null, authorities);
|
||||
|
||||
// 使用用户名字符串作为Principal,而不是User对象
|
||||
// 这样 authentication.getName() 会返回用户名字符串
|
||||
UsernamePasswordAuthenticationToken authToken =
|
||||
new UsernamePasswordAuthenticationToken(user.getUsername(), null, authorities);
|
||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||
logger.debug("JWT认证成功,用户: {}, 角色: {}", username, user.getRole());
|
||||
|
||||
@@ -44,6 +44,10 @@ public class PlainTextPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,8 +22,13 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
|
||||
// 支持通过用户名或邮箱登录:先按用户名查找,找不到时按邮箱查找
|
||||
java.util.Optional<com.example.demo.model.User> opt = userRepository.findByUsername(username);
|
||||
if (opt.isEmpty()) {
|
||||
opt = userRepository.findByEmail(username);
|
||||
}
|
||||
|
||||
User user = opt.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
|
||||
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
user.getUsername(),
|
||||
|
||||
@@ -22,13 +22,31 @@ public class ApiResponseHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiResponseHandler.class);
|
||||
private final ObjectMapper objectMapper;
|
||||
private static boolean unirestConfigured = false;
|
||||
|
||||
public ApiResponseHandler() {
|
||||
this.objectMapper = new ObjectMapper();
|
||||
// 设置Unirest超时配置 - 修复HTTP客户端协议异常
|
||||
Unirest.config()
|
||||
.connectTimeout(30000) // 30秒连接超时
|
||||
.socketTimeout(300000); // 5分钟读取超时
|
||||
// 只配置一次Unirest
|
||||
configureUnirest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置Unirest - 使用同步锁确保只配置一次
|
||||
*/
|
||||
private synchronized void configureUnirest() {
|
||||
if (!unirestConfigured) {
|
||||
try {
|
||||
// 设置Unirest超时配置
|
||||
Unirest.config()
|
||||
.connectTimeout(30000) // 30秒连接超时
|
||||
.socketTimeout(300000); // 5分钟读取超时
|
||||
unirestConfigured = true;
|
||||
logger.info("Unirest配置完成");
|
||||
} catch (Exception e) {
|
||||
// 如果配置失败(例如已经配置过),则忽略错误
|
||||
logger.warn("Unirest配置异常,可能已经配置过: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,7 @@ public class ImageToVideoService {
|
||||
@Value("${app.video.output.path:/outputs}")
|
||||
private String outputPath;
|
||||
|
||||
|
||||
/**
|
||||
* 创建图生视频任务
|
||||
*/
|
||||
@@ -395,27 +396,41 @@ public class ImageToVideoService {
|
||||
* 保存图片文件
|
||||
*/
|
||||
private String saveImage(MultipartFile file, String taskId, String type) throws IOException {
|
||||
// 确保上传目录存在
|
||||
Path uploadDir = Paths.get(uploadPath);
|
||||
// 解析上传目录:如果配置的是相对路径,则相对于应用当前工作目录
|
||||
Path uploadDir = Paths.get(uploadPath == null ? "uploads" : uploadPath);
|
||||
if (!uploadDir.isAbsolute()) {
|
||||
uploadDir = Paths.get(System.getProperty("user.dir")).resolve(uploadDir).toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
// 确保上传目录存在(创建所有父目录)
|
||||
if (!Files.exists(uploadDir)) {
|
||||
Files.createDirectories(uploadDir);
|
||||
}
|
||||
|
||||
|
||||
// 创建任务目录
|
||||
Path taskDir = uploadDir.resolve(taskId);
|
||||
Files.createDirectories(taskDir);
|
||||
|
||||
if (!Files.exists(taskDir)) {
|
||||
Files.createDirectories(taskDir);
|
||||
}
|
||||
|
||||
// 生成文件名
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = getFileExtension(originalFilename);
|
||||
String filename = type + "_" + System.currentTimeMillis() + extension;
|
||||
|
||||
// 保存文件
|
||||
|
||||
// 保存文件(覆盖同名文件的行为由 Files.copy 决定,此处不启用 REPLACE_EXISTING)
|
||||
Path filePath = taskDir.resolve(filename);
|
||||
Files.copy(file.getInputStream(), filePath);
|
||||
|
||||
// 返回相对路径,确保路径格式正确
|
||||
return uploadPath + "/" + taskId + "/" + filename;
|
||||
try {
|
||||
Files.copy(file.getInputStream(), filePath);
|
||||
} catch (IOException e) {
|
||||
logger.error("保存上传图片失败: uploadDir={}, taskId={}, fileName={}", uploadDir, taskId, filename, e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// 返回前端可访问的相对URL(由 WebMvcConfig 映射 /uploads/** -> upload 目录)
|
||||
// 确保使用统一的URL前缀 /uploads
|
||||
String urlPath = "/uploads/" + taskId + "/" + filename;
|
||||
return urlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -207,27 +207,27 @@ public class RealAIService {
|
||||
int retryAttempt = 0; // 重试次数计数器
|
||||
|
||||
while (retryCount < maxRetries) {
|
||||
try {
|
||||
// 根据参数选择可用的模型
|
||||
String modelName = selectAvailableImageToVideoModel(aspectRatio, duration, hdMode);
|
||||
|
||||
try {
|
||||
// 根据参数选择可用的模型
|
||||
String modelName = selectAvailableImageToVideoModel(aspectRatio, duration, hdMode);
|
||||
|
||||
// 验证base64数据格式(提取纯Base64数据用于验证)
|
||||
String base64DataForValidation = imageBase64;
|
||||
if (imageBase64.contains(",")) {
|
||||
if (imageBase64.contains(",")) {
|
||||
base64DataForValidation = imageBase64.substring(imageBase64.indexOf(",") + 1);
|
||||
}
|
||||
try {
|
||||
}
|
||||
try {
|
||||
Base64.getDecoder().decode(base64DataForValidation);
|
||||
logger.debug("Base64数据格式验证通过");
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Base64数据格式错误: {}", e.getMessage());
|
||||
throw new RuntimeException("图片数据格式错误");
|
||||
}
|
||||
|
||||
// 根据分辨率选择size参数(用于日志记录)
|
||||
String size = convertAspectRatioToSize(aspectRatio, hdMode);
|
||||
logger.debug("选择的尺寸参数: {}", size);
|
||||
|
||||
logger.debug("Base64数据格式验证通过");
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Base64数据格式错误: {}", e.getMessage());
|
||||
throw new RuntimeException("图片数据格式错误");
|
||||
}
|
||||
|
||||
// 根据分辨率选择size参数(用于日志记录)
|
||||
String size = convertAspectRatioToSize(aspectRatio, hdMode);
|
||||
logger.debug("选择的尺寸参数: {}", size);
|
||||
|
||||
// 使用 Sora2 端点(与文生视频使用相同的端点,参考 Comfly.py 6297 行)
|
||||
String url = aiApiBaseUrl + "/v2/videos/generations";
|
||||
|
||||
@@ -273,35 +273,35 @@ public class RealAIService {
|
||||
|
||||
// 使用流式传输,避免一次性加载整个请求体到内存
|
||||
// 添加额外的请求头以支持大请求体
|
||||
HttpResponse<String> response = Unirest.post(url)
|
||||
.header("Authorization", "Bearer " + aiApiKey)
|
||||
HttpResponse<String> response = Unirest.post(url)
|
||||
.header("Authorization", "Bearer " + aiApiKey)
|
||||
.header("Content-Type", "application/json; charset=UTF-8")
|
||||
.header("Accept", "application/json")
|
||||
.header("Connection", "keep-alive")
|
||||
.body(requestBody)
|
||||
.asString();
|
||||
.body(requestBody)
|
||||
.asString();
|
||||
|
||||
// 添加响应调试日志
|
||||
logger.info("API响应状态: {}", response.getStatus());
|
||||
String responseBodyStr = response.getBody();
|
||||
logger.info("API响应内容(前500字符): {}", responseBodyStr != null && responseBodyStr.length() > 500 ?
|
||||
responseBodyStr.substring(0, 500) : responseBodyStr);
|
||||
// 添加响应调试日志
|
||||
logger.info("API响应状态: {}", response.getStatus());
|
||||
String responseBodyStr = response.getBody();
|
||||
logger.info("API响应内容(前500字符): {}", responseBodyStr != null && responseBodyStr.length() > 500 ?
|
||||
responseBodyStr.substring(0, 500) : responseBodyStr);
|
||||
|
||||
if (response.getStatus() == 200 && responseBodyStr != null) {
|
||||
// 检查响应是否为HTML(可能是认证失败或API端点错误)
|
||||
String trimmedResponse = responseBodyStr.trim();
|
||||
String lowerResponse = trimmedResponse.toLowerCase();
|
||||
if (lowerResponse.startsWith("<!") || lowerResponse.startsWith("<html") ||
|
||||
lowerResponse.contains("<!doctype") || (!trimmedResponse.startsWith("{") && !trimmedResponse.startsWith("["))) {
|
||||
logger.error("API返回HTML页面而不是JSON,可能是认证失败或API端点错误");
|
||||
logger.error("响应前100字符: {}", trimmedResponse.length() > 100 ? trimmedResponse.substring(0, 100) : trimmedResponse);
|
||||
logger.error("请检查:1) API密钥是否正确 2) API端点URL是否正确 3) API服务是否正常运行");
|
||||
throw new RuntimeException("API返回HTML页面,可能是认证失败。请检查API密钥和端点配置");
|
||||
}
|
||||
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> responseBody = objectMapper.readValue(responseBodyStr, Map.class);
|
||||
if (response.getStatus() == 200 && responseBodyStr != null) {
|
||||
// 检查响应是否为HTML(可能是认证失败或API端点错误)
|
||||
String trimmedResponse = responseBodyStr.trim();
|
||||
String lowerResponse = trimmedResponse.toLowerCase();
|
||||
if (lowerResponse.startsWith("<!") || lowerResponse.startsWith("<html") ||
|
||||
lowerResponse.contains("<!doctype") || (!trimmedResponse.startsWith("{") && !trimmedResponse.startsWith("["))) {
|
||||
logger.error("API返回HTML页面而不是JSON,可能是认证失败或API端点错误");
|
||||
logger.error("响应前100字符: {}", trimmedResponse.length() > 100 ? trimmedResponse.substring(0, 100) : trimmedResponse);
|
||||
logger.error("请检查:1) API密钥是否正确 2) API端点URL是否正确 3) API服务是否正常运行");
|
||||
throw new RuntimeException("API返回HTML页面,可能是认证失败。请检查API密钥和端点配置");
|
||||
}
|
||||
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> responseBody = objectMapper.readValue(responseBodyStr, Map.class);
|
||||
|
||||
// Sora2 API 使用 task_id 字段表示成功(与文生视频相同格式)
|
||||
if (responseBody.containsKey("task_id")) {
|
||||
@@ -312,7 +312,7 @@ public class RealAIService {
|
||||
result.put("data", responseBody);
|
||||
result.put("task_id", responseBody.get("task_id"));
|
||||
return result;
|
||||
} else {
|
||||
} else {
|
||||
// 处理错误响应
|
||||
logger.error("图生视频任务提交失败,响应中缺少task_id: {}", responseBody);
|
||||
String errorMsg = "未知错误";
|
||||
@@ -320,19 +320,19 @@ public class RealAIService {
|
||||
errorMsg = responseBody.get("message").toString();
|
||||
}
|
||||
throw new RuntimeException("任务提交失败: " + errorMsg);
|
||||
}
|
||||
} catch (com.fasterxml.jackson.core.JsonParseException e) {
|
||||
logger.error("解析API响应为JSON失败,响应内容可能是HTML或其他格式", e);
|
||||
logger.error("响应内容前200字符: {}", responseBodyStr.length() > 200 ?
|
||||
responseBodyStr.substring(0, 200) : responseBodyStr);
|
||||
throw new RuntimeException("API返回非JSON响应,可能是认证失败。请检查API密钥和端点配置");
|
||||
}
|
||||
} else {
|
||||
logger.error("图生视频任务提交失败,HTTP状态: {}", response.getStatus());
|
||||
throw new RuntimeException("任务提交失败,HTTP状态: " + response.getStatus());
|
||||
} catch (com.fasterxml.jackson.core.JsonParseException e) {
|
||||
logger.error("解析API响应为JSON失败,响应内容可能是HTML或其他格式", e);
|
||||
logger.error("响应内容前200字符: {}", responseBodyStr.length() > 200 ?
|
||||
responseBodyStr.substring(0, 200) : responseBodyStr);
|
||||
throw new RuntimeException("API返回非JSON响应,可能是认证失败。请检查API密钥和端点配置");
|
||||
}
|
||||
} else {
|
||||
logger.error("图生视频任务提交失败,HTTP状态: {}", response.getStatus());
|
||||
throw new RuntimeException("任务提交失败,HTTP状态: " + response.getStatus());
|
||||
}
|
||||
|
||||
} catch (UnirestException e) {
|
||||
} catch (UnirestException e) {
|
||||
retryCount++;
|
||||
retryAttempt++;
|
||||
Throwable cause = e.getCause();
|
||||
@@ -391,11 +391,11 @@ public class RealAIService {
|
||||
logger.error("建议: 1) 检查网络连接 2) 检查请求体大小是否过大 3) 联系API服务提供商检查服务器状态");
|
||||
throw new RuntimeException("提交任务失败: " + errorMessage, e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
// 其他不可重试的错误直接抛出
|
||||
logger.error("提交图生视频任务异常", e);
|
||||
logger.error("提交图生视频任务异常", e);
|
||||
throw new RuntimeException("提交任务失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 理论上不会到达这里,但为了编译通过
|
||||
@@ -728,7 +728,7 @@ public class RealAIService {
|
||||
logger.debug("验证图片格式完成,原始数量: {}, 验证后数量: {}", images.size(), validatedImages.size());
|
||||
return validatedImages;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将图片文件转换为Base64
|
||||
*/
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import com.example.demo.model.StoryboardVideoTask;
|
||||
import com.example.demo.model.TaskStatus;
|
||||
import com.example.demo.repository.StoryboardVideoTaskRepository;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -56,7 +57,16 @@ public class StoryboardVideoService {
|
||||
|
||||
@Autowired
|
||||
private TaskQueueService taskQueueService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private UserWorkService userWorkService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private TaskStatusPollingService taskStatusPollingService;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@@ -352,6 +362,45 @@ public class StoryboardVideoService {
|
||||
task.updateStatus(StoryboardVideoTask.TaskStatus.PROCESSING);
|
||||
taskRepository.save(task);
|
||||
logger.info("任务状态已更新为处理中: {}", taskId);
|
||||
|
||||
// 创建 TaskStatus 记录用于跟踪分镜图生成状态
|
||||
try {
|
||||
// 分镜图生成是同步的,不需要 externalTaskId
|
||||
TaskStatus taskStatus = taskStatusPollingService.createTaskStatus(
|
||||
taskId,
|
||||
task.getUsername(),
|
||||
TaskStatus.TaskType.STORYBOARD_VIDEO,
|
||||
null // 分镜图生成是同步API,不需要外部任务ID
|
||||
);
|
||||
logger.info("TaskStatus 记录已创建: taskId={}, status={}", taskId, taskStatus.getStatus());
|
||||
} catch (Exception e) {
|
||||
logger.error("创建 TaskStatus 记录失败: {}", taskId, e);
|
||||
// 不抛出异常,避免影响主流程
|
||||
}
|
||||
|
||||
// 创建初始的 UserWork 记录(PROCESSING状态),以便前端可以恢复任务
|
||||
try {
|
||||
// 检查是否已存在 UserWork
|
||||
if (!userWorkService.getWorkByTaskId(taskId).isPresent()) {
|
||||
com.example.demo.model.UserWork work = new com.example.demo.model.UserWork();
|
||||
work.setUserId(getUserIdByUsername(task.getUsername()));
|
||||
work.setUsername(task.getUsername());
|
||||
work.setTaskId(taskId);
|
||||
work.setWorkType(com.example.demo.model.UserWork.WorkType.STORYBOARD_VIDEO);
|
||||
work.setTitle(generateTitle(task.getPrompt()));
|
||||
work.setDescription("分镜视频生成中");
|
||||
work.setPrompt(task.getPrompt());
|
||||
work.setAspectRatio(task.getAspectRatio());
|
||||
work.setQuality(task.isHdMode() ? "HD" : "SD");
|
||||
work.setPointsCost(0); // 任务进行中,暂时不计算积分
|
||||
work.setStatus(com.example.demo.model.UserWork.WorkStatus.PROCESSING);
|
||||
userWorkService.createWork(work);
|
||||
logger.info("初始UserWork记录已创建: taskId={}", taskId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("创建初始UserWork记录失败: {}", taskId, e);
|
||||
// 不抛出异常,避免影响主流程
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务状态失败: {}", taskId, e);
|
||||
status.setRollbackOnly();
|
||||
@@ -376,6 +425,20 @@ public class StoryboardVideoService {
|
||||
task.updateProgress(50); // 分镜图生成完成,进度50%
|
||||
taskRepository.save(task);
|
||||
logger.debug("分镜图结果已保存: taskId={}, 图片数量={}", taskId, validatedImageCount);
|
||||
|
||||
// 更新 TaskStatus 为完成状态
|
||||
try {
|
||||
TaskStatus taskStatus = taskStatusPollingService.getTaskStatus(taskId);
|
||||
if (taskStatus != null) {
|
||||
taskStatus.markAsCompleted(mergedImageUrl);
|
||||
taskStatus.setProgress(50); // 分镜图完成,进度50%
|
||||
taskStatusPollingService.saveOrUpdateTaskStatus(taskStatus);
|
||||
logger.info("TaskStatus 已更新为完成: taskId={}", taskId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新 TaskStatus 为完成失败: {}", taskId, e);
|
||||
// 不抛出异常,避免影响主流程
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("保存分镜图结果失败: {}", taskId, e);
|
||||
status.setRollbackOnly();
|
||||
@@ -396,6 +459,19 @@ public class StoryboardVideoService {
|
||||
task.updateStatus(StoryboardVideoTask.TaskStatus.FAILED);
|
||||
task.setErrorMessage(errorMessage);
|
||||
taskRepository.save(task);
|
||||
|
||||
// 更新 TaskStatus 为失败状态
|
||||
try {
|
||||
TaskStatus taskStatus = taskStatusPollingService.getTaskStatus(taskId);
|
||||
if (taskStatus != null) {
|
||||
taskStatus.markAsFailed(errorMessage);
|
||||
taskStatusPollingService.saveOrUpdateTaskStatus(taskStatus);
|
||||
logger.info("TaskStatus 已更新为失败: taskId={}", taskId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新 TaskStatus 为失败失败: {}", taskId, e);
|
||||
// 不抛出异常,避免影响主流程
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务失败状态失败: {}", taskId, e);
|
||||
status.setRollbackOnly();
|
||||
@@ -441,6 +517,15 @@ public class StoryboardVideoService {
|
||||
// 状态保持 PROCESSING,等待用户点击"开始生成"按钮后再生成视频
|
||||
taskRepository.save(task);
|
||||
logger.debug("分镜图结果已保存: taskId={}, 图片数量={}", taskId, validatedImageCount);
|
||||
|
||||
// 创建分镜图作品记录
|
||||
try {
|
||||
userWorkService.createStoryboardImageWork(taskId, mergedImageUrl);
|
||||
logger.info("分镜图作品记录已创建: taskId={}", taskId);
|
||||
} catch (Exception e) {
|
||||
logger.error("创建分镜图作品记录失败: taskId={}", taskId, e);
|
||||
// 不抛出异常,避免影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -864,14 +949,47 @@ public class StoryboardVideoService {
|
||||
}
|
||||
|
||||
if (handledCount > 0 || skippedCount > 0) {
|
||||
logger.info("处理超时分镜图生成任务完成,失败: {}/{},跳过(已生成): {}",
|
||||
logger.info("处理超时分镜图生成任务完成,失败: {}/{},跳过(已生成): {}",
|
||||
handledCount, timeoutTasks.size(), skippedCount);
|
||||
}
|
||||
return handledCount;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("检查超时分镜图生成任务失败", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户名获取用户ID
|
||||
*/
|
||||
private Long getUserIdByUsername(String username) {
|
||||
try {
|
||||
com.example.demo.model.User user = userService.findByUsername(username);
|
||||
if (user != null) {
|
||||
return user.getId();
|
||||
}
|
||||
logger.warn("找不到用户: {}", username);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.error("获取用户ID失败: {}", username, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成作品标题
|
||||
*/
|
||||
private String generateTitle(String prompt) {
|
||||
if (prompt == null || prompt.trim().isEmpty()) {
|
||||
return "未命名作品";
|
||||
}
|
||||
|
||||
// 取提示词的前20个字符作为标题
|
||||
String title = prompt.trim();
|
||||
if (title.length() > 20) {
|
||||
title = title.substring(0, 20) + "...";
|
||||
}
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package com.example.demo.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -77,6 +71,9 @@ public class TaskQueueService {
|
||||
|
||||
@Autowired
|
||||
private VideoConcatService videoConcatService;
|
||||
|
||||
@Autowired
|
||||
private ImageGridService imageGridService;
|
||||
|
||||
@org.springframework.beans.factory.annotation.Value("${app.temp.dir:./temp}")
|
||||
private String tempDir;
|
||||
@@ -258,7 +255,7 @@ public class TaskQueueService {
|
||||
// 创建新的队列任务
|
||||
TaskQueue taskQueue = new TaskQueue(username, taskId, taskType);
|
||||
taskQueue = taskQueueRepository.save(taskQueue);
|
||||
|
||||
|
||||
// 添加到内存阻塞队列(非阻塞,如果队列满会立即返回false,但无界队列不会满)
|
||||
boolean added = taskBlockingQueue.offer(taskQueue);
|
||||
if (added) {
|
||||
@@ -446,7 +443,7 @@ public class TaskQueueService {
|
||||
|
||||
// 更新状态为处理中
|
||||
taskQueue.updateStatus(TaskQueue.QueueStatus.PROCESSING);
|
||||
taskQueueRepository.save(taskQueue);
|
||||
taskQueueRepository.save(taskQueue);
|
||||
logger.debug("任务 {} 状态已更新为PROCESSING(使用悲观锁)", taskId);
|
||||
return Boolean.TRUE;
|
||||
} catch (jakarta.persistence.NoResultException e) {
|
||||
@@ -509,7 +506,7 @@ public class TaskQueueService {
|
||||
task.isHdMode()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用只读事务获取文生视频任务(快速完成,避免连接泄漏)
|
||||
*/
|
||||
@@ -556,15 +553,14 @@ public class TaskQueueService {
|
||||
|
||||
/**
|
||||
* 处理分镜视频任务
|
||||
* 使用分镜图(Base64)调用 sora2 生成视频
|
||||
* 将6张分镜图拼接成一张图片,然后调用图生视频接口生成视频
|
||||
* 使用只读事务快速查询任务信息
|
||||
*/
|
||||
private Map<String, Object> processStoryboardVideoTask(TaskQueue taskQueue) {
|
||||
// 使用只读事务快速查询任务信息
|
||||
StoryboardVideoTask task = getStoryboardVideoTaskWithTransaction(taskQueue.getTaskId());
|
||||
|
||||
// 参考sora2实现:优先使用单独的分镜图片数组(参考Comfly.py 6272-6278行)
|
||||
// 如果存在单独的分镜图片,使用它们;否则回退到网格图
|
||||
// 获取6张分镜图片
|
||||
List<String> images = null;
|
||||
String storyboardImagesJson = task.getStoryboardImages();
|
||||
|
||||
@@ -577,10 +573,10 @@ public class TaskQueueService {
|
||||
);
|
||||
if (parsedImages != null && !parsedImages.isEmpty()) {
|
||||
images = parsedImages;
|
||||
logger.info("使用单独的分镜图片生成视频,图片数量: {}", images.size());
|
||||
logger.info("获取到分镜图片,数量: {}", images.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("解析分镜图片JSON失败,回退到网格图: {}", e.getMessage());
|
||||
logger.warn("解析分镜图片JSON失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,80 +586,90 @@ public class TaskQueueService {
|
||||
if (imageBase64 == null || imageBase64.isEmpty()) {
|
||||
throw new RuntimeException("分镜图未生成,无法生成视频");
|
||||
}
|
||||
// 将网格图转换为单元素数组(参考sora2实现)
|
||||
images = new java.util.ArrayList<>();
|
||||
images.add(imageBase64);
|
||||
// 如果只有网格图,直接使用(已经是拼接后的图片)
|
||||
logger.info("使用网格图生成视频(向后兼容)");
|
||||
}
|
||||
|
||||
// 使用 sora2 生成视频(使用默认时长,每张图片单独生成)
|
||||
// 每张图片的时长:总时长 / 图片数量,但最少2秒
|
||||
int totalDuration = 10; // 总时长10秒
|
||||
int perImageDuration = Math.max(2, totalDuration / images.size()); // 每张图片至少2秒
|
||||
String duration = String.valueOf(perImageDuration);
|
||||
|
||||
logger.info("开始为每张图片单独提交视频任务,图片数量: {}, 每张图片时长: {}秒", images.size(), duration);
|
||||
|
||||
// 为每张图片单独提交视频任务
|
||||
List<String> videoTaskIds = new java.util.ArrayList<>();
|
||||
for (int i = 0; i < images.size(); i++) {
|
||||
String image = images.get(i);
|
||||
try {
|
||||
// 为单张图片提交视频任务(使用单元素列表)
|
||||
List<String> singleImageList = new java.util.ArrayList<>();
|
||||
singleImageList.add(image);
|
||||
|
||||
Map<String, Object> result = realAIService.submitStoryboardVideoTask(
|
||||
task.getPrompt(),
|
||||
singleImageList,
|
||||
task.getAspectRatio(),
|
||||
duration,
|
||||
task.isHdMode()
|
||||
);
|
||||
|
||||
if (result != null && result.containsKey("task_id")) {
|
||||
String videoTaskId = result.get("task_id").toString();
|
||||
videoTaskIds.add(videoTaskId);
|
||||
logger.info("图片 {} 的视频任务提交成功,task_id: {}", i + 1, videoTaskId);
|
||||
} else {
|
||||
logger.error("图片 {} 的视频任务提交失败,未返回task_id", i + 1);
|
||||
throw new RuntimeException("图片 " + (i + 1) + " 的视频任务提交失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("为图片 {} 提交视频任务失败: {}", i + 1, e.getMessage(), e);
|
||||
throw new RuntimeException("为图片 " + (i + 1) + " 提交视频任务失败: " + e.getMessage(), e);
|
||||
|
||||
// 直接使用网格图调用图生视频接口
|
||||
Map<String, Object> result = realAIService.submitImageToVideoTask(
|
||||
task.getPrompt(),
|
||||
imageBase64,
|
||||
task.getAspectRatio(),
|
||||
"10", // 默认10秒
|
||||
task.isHdMode()
|
||||
);
|
||||
|
||||
if (result != null && result.containsKey("task_id")) {
|
||||
String videoTaskId = result.get("task_id").toString();
|
||||
// 保存视频任务ID到数据库
|
||||
saveVideoTaskId(taskQueue.getTaskId(), videoTaskId);
|
||||
logger.info("分镜视频任务提交成功,task_id: {}", videoTaskId);
|
||||
return result;
|
||||
} else {
|
||||
throw new RuntimeException("图生视频任务提交失败,未返回task_id");
|
||||
}
|
||||
}
|
||||
|
||||
// 保存所有视频任务ID到数据库
|
||||
// 确保有6张图片
|
||||
if (images.size() < 6) {
|
||||
throw new RuntimeException("分镜图片数量不足,需要6张,当前只有" + images.size() + "张");
|
||||
}
|
||||
|
||||
// 只取前6张图片
|
||||
if (images.size() > 6) {
|
||||
logger.warn("分镜图片数量超过6张,只取前6张进行拼接");
|
||||
images = images.subList(0, 6);
|
||||
}
|
||||
|
||||
logger.info("开始将6张分镜图拼接成一张图片...");
|
||||
|
||||
// 将6张图片拼接成一张图片
|
||||
String mergedImageBase64;
|
||||
try {
|
||||
mergedImageBase64 = imageGridService.mergeImagesToGrid(images, 3); // 3列2行布局
|
||||
logger.info("6张分镜图拼接完成");
|
||||
} catch (Exception e) {
|
||||
logger.error("拼接分镜图失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("拼接分镜图失败: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
// 使用拼接后的图片调用图生视频接口
|
||||
logger.info("使用拼接后的图片调用图生视频接口...");
|
||||
Map<String, Object> result = realAIService.submitImageToVideoTask(
|
||||
task.getPrompt(),
|
||||
mergedImageBase64,
|
||||
task.getAspectRatio(),
|
||||
"10", // 默认10秒
|
||||
task.isHdMode()
|
||||
);
|
||||
|
||||
if (result != null && result.containsKey("task_id")) {
|
||||
String videoTaskId = result.get("task_id").toString();
|
||||
// 保存视频任务ID到数据库
|
||||
saveVideoTaskId(taskQueue.getTaskId(), videoTaskId);
|
||||
logger.info("分镜视频任务提交成功,task_id: {}", videoTaskId);
|
||||
return result;
|
||||
} else {
|
||||
throw new RuntimeException("图生视频任务提交失败,未返回task_id");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存视频任务ID到数据库
|
||||
*/
|
||||
private void saveVideoTaskId(String taskId, String videoTaskId) {
|
||||
try {
|
||||
String videoTaskIdsJson = objectMapper.writeValueAsString(videoTaskIds);
|
||||
// 使用事务模板保存
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
StoryboardVideoTask currentTask = storyboardVideoTaskRepository.findByTaskId(taskQueue.getTaskId())
|
||||
.orElseThrow(() -> new RuntimeException("任务不存在: " + taskQueue.getTaskId()));
|
||||
currentTask.setVideoTaskIds(videoTaskIdsJson);
|
||||
StoryboardVideoTask currentTask = storyboardVideoTaskRepository.findByTaskId(taskId)
|
||||
.orElseThrow(() -> new RuntimeException("任务不存在: " + taskId));
|
||||
// 保存为单个任务ID(不再使用videoTaskIds数组)
|
||||
currentTask.setRealTaskId(videoTaskId);
|
||||
storyboardVideoTaskRepository.save(currentTask);
|
||||
logger.info("已保存{}个视频任务ID到数据库", videoTaskIds.size());
|
||||
logger.info("已保存视频任务ID到数据库: {}", videoTaskId);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("保存视频任务ID失败: {}", e.getMessage(), e);
|
||||
// 不抛出异常,因为任务已经提交成功
|
||||
}
|
||||
|
||||
// 返回第一个任务ID(用于兼容现有逻辑)
|
||||
Map<String, Object> result = new java.util.HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("task_id", videoTaskIds.isEmpty() ? null : videoTaskIds.get(0));
|
||||
result.put("video_task_ids", videoTaskIds); // 包含所有任务ID
|
||||
result.put("data", new java.util.HashMap<String, Object>() {{
|
||||
put("task_id", videoTaskIds.isEmpty() ? null : videoTaskIds.get(0));
|
||||
}});
|
||||
|
||||
logger.info("所有视频任务提交完成,共{}个任务,第一个任务ID: {}",
|
||||
videoTaskIds.size(), videoTaskIds.isEmpty() ? "无" : videoTaskIds.get(0));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -835,6 +841,15 @@ public class TaskQueueService {
|
||||
public List<TaskQueue> getTasksToCheck() {
|
||||
return taskQueueRepository.findTasksToCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有待处理的任务(快速检查,只统计数量)
|
||||
* @return true 如果有待处理任务,false 否则
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public boolean hasTasksToCheck() {
|
||||
return taskQueueRepository.countTasksToCheck() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查单个任务状态 - 公共方法
|
||||
@@ -859,7 +874,7 @@ public class TaskQueueService {
|
||||
logger.debug("任务 {} 正在被其他线程查询,跳过本次查询", taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 特殊处理:分镜视频任务需要检查多个视频任务
|
||||
if (taskQueue.getTaskType() == TaskQueue.TaskType.STORYBOARD_VIDEO) {
|
||||
@@ -870,7 +885,7 @@ public class TaskQueueService {
|
||||
if (taskQueue.getRealTaskId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 查询外部API状态
|
||||
Map<String, Object> statusResponse = realAIService.getTaskStatus(taskQueue.getRealTaskId());
|
||||
|
||||
@@ -947,12 +962,12 @@ public class TaskQueueService {
|
||||
|
||||
// 2. 检查嵌套的data.data.task_result.videos[0].url(Kling API嵌套格式)
|
||||
if (resultUrl == null) {
|
||||
Object nestedData = taskData.get("data");
|
||||
if (nestedData instanceof Map) {
|
||||
Map<?, ?> nestedDataMap = (Map<?, ?>) nestedData;
|
||||
Object innerData = nestedDataMap.get("data");
|
||||
if (innerData instanceof Map) {
|
||||
Map<?, ?> innerDataMap = (Map<?, ?>) innerData;
|
||||
Object nestedData = taskData.get("data");
|
||||
if (nestedData instanceof Map) {
|
||||
Map<?, ?> nestedDataMap = (Map<?, ?>) nestedData;
|
||||
Object innerData = nestedDataMap.get("data");
|
||||
if (innerData instanceof Map) {
|
||||
Map<?, ?> innerDataMap = (Map<?, ?>) innerData;
|
||||
|
||||
// 检查 task_result.videos[0].url
|
||||
Object taskResult = innerDataMap.get("task_result");
|
||||
@@ -973,17 +988,17 @@ public class TaskQueueService {
|
||||
|
||||
// 如果上面没找到,检查output字段
|
||||
if (resultUrl == null) {
|
||||
Object output = innerDataMap.get("output");
|
||||
if (output != null) {
|
||||
resultUrl = output.toString();
|
||||
Object output = innerDataMap.get("output");
|
||||
if (output != null) {
|
||||
resultUrl = output.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果深层嵌套没有找到,检查当前层的output
|
||||
if (resultUrl == null) {
|
||||
Object output = nestedDataMap.get("output");
|
||||
if (output != null) {
|
||||
resultUrl = output.toString();
|
||||
}
|
||||
// 如果深层嵌套没有找到,检查当前层的output
|
||||
if (resultUrl == null) {
|
||||
Object output = nestedDataMap.get("output");
|
||||
if (output != null) {
|
||||
resultUrl = output.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1070,8 +1085,9 @@ public class TaskQueueService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查分镜视频任务的多个视频任务状态
|
||||
* 当所有视频任务完成后,下载并拼接视频
|
||||
* 检查分镜视频任务状态
|
||||
* 优化:现在只生成一个视频任务(将6张分镜图拼接后调用图生视频接口)
|
||||
* 不再需要视频拼接逻辑
|
||||
*/
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
private void checkStoryboardVideoTasks(TaskQueue taskQueue) {
|
||||
@@ -1083,229 +1099,22 @@ public class TaskQueueService {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取所有视频任务ID
|
||||
String videoTaskIdsJson = storyboardTask.getVideoTaskIds();
|
||||
if (videoTaskIdsJson == null || videoTaskIdsJson.isEmpty()) {
|
||||
// 如果没有视频任务ID,使用旧的realTaskId(向后兼容)
|
||||
if (taskQueue.getRealTaskId() != null) {
|
||||
// 使用旧的单任务逻辑
|
||||
checkTaskStatusInternalForSingleTask(taskQueue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析视频任务ID列表
|
||||
List<String> videoTaskIds;
|
||||
try {
|
||||
videoTaskIds = objectMapper.readValue(
|
||||
videoTaskIdsJson,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class)
|
||||
);
|
||||
} catch (Exception e) {
|
||||
logger.error("解析视频任务ID失败: {}", taskQueue.getTaskId(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (videoTaskIds == null || videoTaskIds.isEmpty()) {
|
||||
logger.warn("视频任务ID列表为空: {}", taskQueue.getTaskId());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("检查分镜视频任务的{}个视频任务状态: {}", videoTaskIds.size(), taskQueue.getTaskId());
|
||||
|
||||
// 检查所有视频任务状态
|
||||
List<String> completedVideoUrls = new java.util.ArrayList<>();
|
||||
boolean allCompleted = true;
|
||||
boolean hasFailed = false;
|
||||
|
||||
for (String videoTaskId : videoTaskIds) {
|
||||
try {
|
||||
Map<String, Object> statusResponse = realAIService.getTaskStatus(videoTaskId);
|
||||
if (statusResponse == null || !statusResponse.containsKey("data")) {
|
||||
allCompleted = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
Object data = statusResponse.get("data");
|
||||
Map<?, ?> taskData = null;
|
||||
if (data instanceof Map) {
|
||||
taskData = (Map<?, ?>) data;
|
||||
} else if (data instanceof List) {
|
||||
List<?> dataList = (List<?>) data;
|
||||
if (!dataList.isEmpty() && dataList.get(0) instanceof Map) {
|
||||
taskData = (Map<?, ?>) dataList.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (taskData != null) {
|
||||
String status = taskData.get("status") != null ?
|
||||
taskData.get("status").toString().toUpperCase() : "";
|
||||
String videoUrl = extractVideoUrl(taskData);
|
||||
|
||||
if ("COMPLETED".equals(status) || "SUCCESS".equals(status) ||
|
||||
(videoUrl != null && !videoUrl.trim().isEmpty() && !videoUrl.equals("null"))) {
|
||||
if (videoUrl != null && !videoUrl.trim().isEmpty() && !videoUrl.equals("null")) {
|
||||
completedVideoUrls.add(videoUrl);
|
||||
logger.debug("视频任务 {} 已完成,URL: {}", videoTaskId, videoUrl.length() > 50 ? videoUrl.substring(0, 50) + "..." : videoUrl);
|
||||
} else {
|
||||
allCompleted = false;
|
||||
logger.debug("视频任务 {} 状态为完成但无URL", videoTaskId);
|
||||
}
|
||||
} else if ("FAILED".equals(status) || "ERROR".equals(status)) {
|
||||
hasFailed = true;
|
||||
logger.warn("视频任务 {} 失败", videoTaskId);
|
||||
} else {
|
||||
allCompleted = false;
|
||||
logger.debug("视频任务 {} 仍在处理中,状态: {}", videoTaskId, status);
|
||||
}
|
||||
} else {
|
||||
allCompleted = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("检查视频任务状态失败: {}", videoTaskId, e);
|
||||
allCompleted = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有视频都完成了,下载并拼接
|
||||
if (allCompleted && completedVideoUrls.size() == videoTaskIds.size() && !hasFailed) {
|
||||
logger.info("所有视频任务已完成,开始下载并拼接: {} 个视频", completedVideoUrls.size());
|
||||
|
||||
// 检查是否有视频URL
|
||||
if (completedVideoUrls.isEmpty()) {
|
||||
logger.error("所有视频任务完成但没有视频URL");
|
||||
updateTaskAsFailed(taskQueue, "所有视频任务完成但没有视频URL");
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存所有视频URL到数据库
|
||||
try {
|
||||
String videoUrlsJson = objectMapper.writeValueAsString(completedVideoUrls);
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
StoryboardVideoTask currentTask = storyboardVideoTaskRepository.findByTaskId(taskQueue.getTaskId())
|
||||
.orElseThrow(() -> new RuntimeException("任务不存在: " + taskQueue.getTaskId()));
|
||||
currentTask.setVideoUrls(videoUrlsJson);
|
||||
storyboardVideoTaskRepository.save(currentTask);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("保存视频URL失败: {}", taskQueue.getTaskId(), e);
|
||||
}
|
||||
|
||||
// 尝试下载并拼接视频
|
||||
String finalVideoUrl = null;
|
||||
try {
|
||||
// 检查FFmpeg是否可用
|
||||
if (!videoConcatService.isFFmpegAvailable()) {
|
||||
String errorMsg = "FFmpeg不可用,无法拼接视频。请确保FFmpeg已正确安装并配置";
|
||||
logger.error("{}: taskId={}", errorMsg, taskQueue.getTaskId());
|
||||
updateTaskAsFailed(taskQueue, errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建临时输出文件路径
|
||||
String tempOutputFileName = "storyboard_" + taskQueue.getTaskId() + "_" + System.currentTimeMillis() + ".mp4";
|
||||
String outputPath = tempDir + File.separator + tempOutputFileName;
|
||||
|
||||
// 下载并拼接视频
|
||||
boolean concatSuccess = videoConcatService.downloadAndConcatVideos(completedVideoUrls, outputPath);
|
||||
|
||||
if (concatSuccess && new java.io.File(outputPath).exists()) {
|
||||
// 保存拼接后的视频到可访问的位置
|
||||
try {
|
||||
// 处理上传路径:如果是相对路径,转换为绝对路径
|
||||
Path uploadDirPath = Paths.get(uploadPath);
|
||||
if (!uploadDirPath.isAbsolute()) {
|
||||
// 相对路径:基于应用运行目录
|
||||
uploadDirPath = Paths.get(System.getProperty("user.dir"), uploadPath);
|
||||
}
|
||||
|
||||
// 确保上传目录存在
|
||||
if (!Files.exists(uploadDirPath)) {
|
||||
Files.createDirectories(uploadDirPath);
|
||||
}
|
||||
|
||||
// 创建任务目录
|
||||
String taskId = taskQueue.getTaskId();
|
||||
Path taskDir = uploadDirPath.resolve("storyboard_videos").resolve(taskId);
|
||||
Files.createDirectories(taskDir);
|
||||
|
||||
// 生成文件名
|
||||
String outputFileName = "storyboard_" + taskId + "_" + System.currentTimeMillis() + ".mp4";
|
||||
|
||||
// 保存文件
|
||||
Path sourcePath = Paths.get(outputPath);
|
||||
Path targetPath = taskDir.resolve(outputFileName);
|
||||
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// 生成可访问的URL(相对路径,通过静态资源服务访问)
|
||||
// 格式:/uploads/storyboard_videos/{taskId}/{filename}
|
||||
// 注意:URL路径始终使用正斜杠,与操作系统无关
|
||||
String relativePath = "uploads/storyboard_videos/" + taskId + "/" + outputFileName;
|
||||
// 确保URL以 / 开头
|
||||
if (!relativePath.startsWith("/")) {
|
||||
relativePath = "/" + relativePath;
|
||||
}
|
||||
finalVideoUrl = relativePath;
|
||||
|
||||
logger.info("视频拼接成功,已保存到: {}", targetPath);
|
||||
logger.info("视频访问URL: {}", finalVideoUrl);
|
||||
|
||||
// 清理临时文件
|
||||
try {
|
||||
Files.deleteIfExists(sourcePath);
|
||||
logger.debug("已清理临时文件: {}", sourcePath);
|
||||
} catch (IOException e) {
|
||||
logger.warn("清理临时文件失败: {}", sourcePath, e);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("保存拼接后的视频失败: {}", outputPath, e);
|
||||
// 如果保存失败,使用第一个视频URL作为备用方案
|
||||
finalVideoUrl = completedVideoUrls.get(0);
|
||||
logger.warn("使用第一个视频URL作为备用方案: {}", finalVideoUrl);
|
||||
}
|
||||
} else {
|
||||
String errorMsg = "视频拼接失败:FFmpeg执行失败或输出文件不存在";
|
||||
logger.error("{}: taskId={}", errorMsg, taskQueue.getTaskId());
|
||||
updateTaskAsFailed(taskQueue, errorMsg);
|
||||
return;
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
String errorMsg = "视频拼接失败:内存不足。请增加JVM堆内存或减少视频数量";
|
||||
logger.error("{}: taskId={}", errorMsg, taskQueue.getTaskId(), e);
|
||||
updateTaskAsFailed(taskQueue, errorMsg);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
String errorMsg = "视频拼接过程出错: " + e.getMessage();
|
||||
logger.error("{}: taskId={}", errorMsg, taskQueue.getTaskId(), e);
|
||||
updateTaskAsFailed(taskQueue, errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (finalVideoUrl != null && !finalVideoUrl.isEmpty()) {
|
||||
// 更新任务为完成状态
|
||||
updateTaskAsCompleted(taskQueue, finalVideoUrl);
|
||||
} else {
|
||||
logger.error("无法获取最终视频URL");
|
||||
updateTaskAsFailed(taskQueue, "视频拼接失败:无法获取最终视频URL");
|
||||
}
|
||||
} else if (hasFailed) {
|
||||
// 如果有任务失败,标记整个任务失败
|
||||
String errorMessage = "部分视频任务生成失败";
|
||||
updateTaskAsFailed(taskQueue, errorMessage);
|
||||
// 使用realTaskId检查单个视频任务状态(现在只有一个视频任务)
|
||||
String realTaskId = storyboardTask.getRealTaskId();
|
||||
if (realTaskId == null || realTaskId.isEmpty()) {
|
||||
// 向后兼容:检查taskQueue的realTaskId
|
||||
if (taskQueue.getRealTaskId() != null && !taskQueue.getRealTaskId().isEmpty()) {
|
||||
realTaskId = taskQueue.getRealTaskId();
|
||||
} else {
|
||||
// 更新进度(基于已完成的视频数量)
|
||||
if (!videoTaskIds.isEmpty()) {
|
||||
int progress = (completedVideoUrls.size() * 100) / videoTaskIds.size();
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
StoryboardVideoTask currentTask = storyboardVideoTaskRepository.findByTaskId(taskQueue.getTaskId())
|
||||
.orElseThrow(() -> new RuntimeException("任务不存在: " + taskQueue.getTaskId()));
|
||||
currentTask.updateProgress(Math.min(100, progress + 50)); // 50%是分镜图生成,剩余50%是视频生成
|
||||
storyboardVideoTaskRepository.save(currentTask);
|
||||
});
|
||||
logger.warn("分镜视频任务没有realTaskId: {}", taskQueue.getTaskId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查单个视频任务状态(使用图生视频任务的检查逻辑)
|
||||
logger.debug("检查分镜视频任务状态: taskId={}, realTaskId={}", taskQueue.getTaskId(), realTaskId);
|
||||
checkTaskStatusInternalForSingleTask(taskQueue);
|
||||
|
||||
// 增加检查次数
|
||||
incrementCheckCountWithTransaction(taskQueue.getTaskId());
|
||||
} catch (Exception e) {
|
||||
@@ -1450,31 +1259,31 @@ public class TaskQueueService {
|
||||
|
||||
// 扣除冻结的积分(内部已处理重复扣除的情况)
|
||||
try {
|
||||
userService.deductFrozenPoints(taskQueue.getTaskId());
|
||||
userService.deductFrozenPoints(taskQueue.getTaskId());
|
||||
} catch (Exception e) {
|
||||
logger.debug("扣除积分失败(可能已扣除): {}", taskQueue.getTaskId());
|
||||
// 积分扣除失败不影响任务完成状态
|
||||
}
|
||||
|
||||
// 更新原始任务状态
|
||||
updateOriginalTaskStatus(taskQueue, "COMPLETED", resultUrl, null);
|
||||
|
||||
// 创建用户作品 - 在最后执行,避免影响主要流程
|
||||
if (resultUrl != null && !resultUrl.isEmpty()) {
|
||||
try {
|
||||
// 更新原始任务状态
|
||||
updateOriginalTaskStatus(taskQueue, "COMPLETED", resultUrl, null);
|
||||
|
||||
// 创建用户作品 - 在最后执行,避免影响主要流程
|
||||
if (resultUrl != null && !resultUrl.isEmpty()) {
|
||||
try {
|
||||
userWorkService.createWorkFromTask(taskQueue.getTaskId(), resultUrl);
|
||||
} catch (Exception workException) {
|
||||
} catch (Exception workException) {
|
||||
// 如果是重复创建异常,静默处理
|
||||
if (workException.getMessage() == null ||
|
||||
(!workException.getMessage().contains("已存在") &&
|
||||
!workException.getMessage().contains("Duplicate entry"))) {
|
||||
logger.warn("创建用户作品失败: {}", taskQueue.getTaskId());
|
||||
}
|
||||
// 作品创建失败不影响任务完成状态
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务完成状态失败: {}", taskQueue.getTaskId(), e);
|
||||
// 作品创建失败不影响任务完成状态
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务完成状态失败: {}", taskQueue.getTaskId(), e);
|
||||
status.setRollbackOnly();
|
||||
throw e;
|
||||
}
|
||||
@@ -1493,18 +1302,18 @@ public class TaskQueueService {
|
||||
try {
|
||||
// 使用 TransactionTemplate 确保在事务中执行
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
try {
|
||||
taskQueue.updateStatus(TaskQueue.QueueStatus.FAILED);
|
||||
taskQueue.setErrorMessage(errorMessage);
|
||||
taskQueueRepository.save(taskQueue);
|
||||
try {
|
||||
taskQueue.updateStatus(TaskQueue.QueueStatus.FAILED);
|
||||
taskQueue.setErrorMessage(errorMessage);
|
||||
taskQueueRepository.save(taskQueue);
|
||||
|
||||
// 返还冻结的积分
|
||||
userService.returnFrozenPoints(taskQueue.getTaskId());
|
||||
// 返还冻结的积分
|
||||
userService.returnFrozenPoints(taskQueue.getTaskId());
|
||||
|
||||
// 更新原始任务状态
|
||||
updateOriginalTaskStatus(taskQueue, "FAILED", null, errorMessage);
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务失败状态失败: {}", taskQueue.getTaskId(), e);
|
||||
// 更新原始任务状态
|
||||
updateOriginalTaskStatus(taskQueue, "FAILED", null, errorMessage);
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务失败状态失败: {}", taskQueue.getTaskId(), e);
|
||||
status.setRollbackOnly();
|
||||
throw e;
|
||||
}
|
||||
@@ -1523,18 +1332,18 @@ public class TaskQueueService {
|
||||
try {
|
||||
// 使用 TransactionTemplate 确保在事务中执行
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
try {
|
||||
taskQueue.updateStatus(TaskQueue.QueueStatus.TIMEOUT);
|
||||
taskQueue.setErrorMessage("任务处理超时");
|
||||
taskQueueRepository.save(taskQueue);
|
||||
try {
|
||||
taskQueue.updateStatus(TaskQueue.QueueStatus.TIMEOUT);
|
||||
taskQueue.setErrorMessage("任务处理超时");
|
||||
taskQueueRepository.save(taskQueue);
|
||||
|
||||
// 返还冻结的积分
|
||||
userService.returnFrozenPoints(taskQueue.getTaskId());
|
||||
// 返还冻结的积分
|
||||
userService.returnFrozenPoints(taskQueue.getTaskId());
|
||||
|
||||
// 更新原始任务状态
|
||||
updateOriginalTaskStatus(taskQueue, "FAILED", null, "任务处理超时");
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务超时状态失败: {}", taskQueue.getTaskId(), e);
|
||||
// 更新原始任务状态
|
||||
updateOriginalTaskStatus(taskQueue, "FAILED", null, "任务处理超时");
|
||||
} catch (Exception e) {
|
||||
logger.error("更新任务超时状态失败: {}", taskQueue.getTaskId(), e);
|
||||
status.setRollbackOnly();
|
||||
throw e;
|
||||
}
|
||||
@@ -1623,7 +1432,7 @@ public class TaskQueueService {
|
||||
storyboardVideoTaskRepository.save(task);
|
||||
}
|
||||
// 如果新的也是图片,保留原来的分镜图
|
||||
} else {
|
||||
} else {
|
||||
// 当前不是分镜图,直接更新
|
||||
task.setResultUrl(resultUrl);
|
||||
storyboardVideoTaskRepository.save(task);
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -38,24 +37,28 @@ public class TaskStatusPollingService {
|
||||
private String apiBaseUrl;
|
||||
|
||||
/**
|
||||
* 每2分钟执行一次轮询查询任务状态
|
||||
* 固定间隔:120000毫秒 = 2分钟
|
||||
* (Scheduling disabled) 原先每2分钟执行一次轮询查询任务状态。
|
||||
* 注意:调度已集中到 `TaskQueueScheduler.checkTaskStatuses()`,以避免重复并发查询。
|
||||
* 如果你更希望这个服务自己轮询,可以将 @Scheduled 注解恢复到此方法。
|
||||
*/
|
||||
@Scheduled(fixedRate = 120000) // 2分钟 = 120000毫秒
|
||||
// @Scheduled(fixedRate = 120000) // 2分钟 = 120000毫秒
|
||||
public void pollTaskStatuses() {
|
||||
logger.info("=== 开始执行任务状态轮询查询 (每2分钟) ===");
|
||||
|
||||
try {
|
||||
// 查找需要轮询的任务(状态为PROCESSING且创建时间超过2分钟)
|
||||
LocalDateTime cutoffTime = LocalDateTime.now().minusMinutes(2);
|
||||
List<TaskStatus> tasksToPoll = taskStatusRepository.findTasksNeedingPolling(cutoffTime);
|
||||
|
||||
logger.info("找到 {} 个需要轮询查询的任务", tasksToPoll.size());
|
||||
|
||||
if (tasksToPoll.isEmpty()) {
|
||||
logger.debug("当前没有需要轮询的任务");
|
||||
// 先做一次计数,避免在无任务时加载实体列表
|
||||
long needCount = taskStatusRepository.countTasksNeedingPolling(cutoffTime);
|
||||
|
||||
logger.info("需要轮询查询的任务数量: {}", needCount);
|
||||
|
||||
if (needCount == 0) {
|
||||
logger.debug("当前没有需要轮询的任务(count=0)");
|
||||
return;
|
||||
}
|
||||
|
||||
List<TaskStatus> tasksToPoll = taskStatusRepository.findTasksNeedingPolling(cutoffTime);
|
||||
|
||||
// 逐个轮询任务状态
|
||||
for (TaskStatus task : tasksToPoll) {
|
||||
@@ -223,19 +226,28 @@ public class TaskStatusPollingService {
|
||||
@Transactional
|
||||
public boolean cancelTask(String taskId, String username) {
|
||||
TaskStatus task = taskStatusRepository.findByTaskId(taskId).orElse(null);
|
||||
|
||||
|
||||
if (task == null || !task.getUsername().equals(username)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (task.getStatus() == TaskStatus.Status.PROCESSING) {
|
||||
task.setStatus(TaskStatus.Status.CANCELLED);
|
||||
task.setUpdatedAt(LocalDateTime.now());
|
||||
taskStatusRepository.save(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存或更新任务状态
|
||||
*/
|
||||
@Transactional
|
||||
public TaskStatus saveOrUpdateTaskStatus(TaskStatus taskStatus) {
|
||||
taskStatus.setUpdatedAt(LocalDateTime.now());
|
||||
return taskStatusRepository.save(taskStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,6 @@ public class TextToVideoService {
|
||||
if (prompt == null || prompt.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("文本描述不能为空");
|
||||
}
|
||||
if (prompt.trim().length() > 1000) {
|
||||
throw new IllegalArgumentException("文本描述不能超过1000个字符");
|
||||
}
|
||||
if (duration < 1 || duration > 60) {
|
||||
throw new IllegalArgumentException("视频时长必须在1-60秒之间");
|
||||
}
|
||||
|
||||
@@ -174,6 +174,46 @@ public class UserWorkService {
|
||||
return work;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建分镜图作品(从分镜视频任务的分镜图阶段)
|
||||
*/
|
||||
@Transactional
|
||||
public UserWork createStoryboardImageWork(String taskId, String imageUrl) {
|
||||
// 检查是否已存在分镜图作品(避免重复创建)
|
||||
Optional<UserWork> existingWork = userWorkRepository.findByTaskId(taskId + "_image");
|
||||
if (existingWork.isPresent()) {
|
||||
logger.info("分镜图作品已存在,跳过创建: taskId={}, workId={}", taskId, existingWork.get().getId());
|
||||
return existingWork.get();
|
||||
}
|
||||
|
||||
// 获取分镜视频任务
|
||||
Optional<StoryboardVideoTask> taskOpt = storyboardVideoTaskRepository.findByTaskId(taskId);
|
||||
if (!taskOpt.isPresent()) {
|
||||
throw new RuntimeException("找不到分镜视频任务: " + taskId);
|
||||
}
|
||||
|
||||
StoryboardVideoTask task = taskOpt.get();
|
||||
|
||||
UserWork work = new UserWork();
|
||||
work.setUserId(getUserIdByUsername(task.getUsername()));
|
||||
work.setUsername(task.getUsername());
|
||||
work.setTaskId(taskId + "_image"); // 添加后缀区分分镜图和视频
|
||||
work.setWorkType(UserWork.WorkType.STORYBOARD_IMAGE);
|
||||
work.setTitle(generateTitle(task.getPrompt()) + " - 分镜图");
|
||||
work.setDescription("分镜图作品");
|
||||
work.setPrompt(task.getPrompt());
|
||||
work.setResultUrl(imageUrl);
|
||||
work.setAspectRatio(task.getAspectRatio());
|
||||
work.setQuality(task.isHdMode() ? "HD" : "SD");
|
||||
work.setPointsCost(0); // 分镜图不单独扣费
|
||||
work.setStatus(UserWork.WorkStatus.COMPLETED);
|
||||
work.setCompletedAt(LocalDateTime.now());
|
||||
|
||||
work = userWorkRepository.save(work);
|
||||
logger.info("创建分镜图作品成功: {}, 用户: {}", work.getId(), work.getUsername());
|
||||
return work;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户名获取用户ID
|
||||
*/
|
||||
@@ -216,6 +256,14 @@ public class UserWorkService {
|
||||
return userWorkRepository.findByUsernameOrderByCreatedAtDesc(username, pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户正在进行中的作品(包括PROCESSING和PENDING状态)
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public java.util.List<UserWork> getProcessingWorks(String username) {
|
||||
return userWorkRepository.findByUsernameAndProcessingOrPendingOrderByCreatedAtDesc(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户作品详情
|
||||
*/
|
||||
@@ -417,6 +465,36 @@ public class UserWorkService {
|
||||
return userWorkRepository.findByTaskId(taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接创建作品记录
|
||||
*/
|
||||
@Transactional
|
||||
public UserWork createWork(UserWork work) {
|
||||
if (work.getStatus() == null) {
|
||||
work.setStatus(UserWork.WorkStatus.PROCESSING);
|
||||
}
|
||||
if (work.getPointsCost() == null) {
|
||||
work.setPointsCost(0);
|
||||
}
|
||||
if (work.getIsPublic() == null) {
|
||||
work.setIsPublic(Boolean.FALSE);
|
||||
}
|
||||
if (work.getViewCount() == null) {
|
||||
work.setViewCount(0);
|
||||
}
|
||||
if (work.getLikeCount() == null) {
|
||||
work.setLikeCount(0);
|
||||
}
|
||||
if (work.getDownloadCount() == null) {
|
||||
work.setDownloadCount(0);
|
||||
}
|
||||
work.setUpdatedAt(LocalDateTime.now());
|
||||
if (work.getCreatedAt() == null) {
|
||||
work.setCreatedAt(LocalDateTime.now());
|
||||
}
|
||||
return userWorkRepository.save(work);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新作品状态
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,14 @@ public class VerificationCodeService {
|
||||
private final ConcurrentHashMap<String, String> verificationCodes = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Long> rateLimits = new ConcurrentHashMap<>();
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
|
||||
/**
|
||||
* 统一归一化邮箱(去首尾空格并小写),避免大小写或空白导致匹配失败
|
||||
*/
|
||||
private String normalizeEmail(String email) {
|
||||
if (email == null) return null;
|
||||
return email.trim().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码长度
|
||||
@@ -77,11 +85,12 @@ public class VerificationCodeService {
|
||||
*/
|
||||
public boolean sendEmailVerificationCode(String email) {
|
||||
try {
|
||||
// 检查发送频率限制
|
||||
String rateLimitKey = "email_rate_limit:" + email;
|
||||
// 归一化邮箱并检查发送频率限制
|
||||
String normEmail = normalizeEmail(email);
|
||||
String rateLimitKey = "email_rate_limit:" + normEmail;
|
||||
Long lastSendTime = rateLimits.get(rateLimitKey);
|
||||
if (lastSendTime != null && System.currentTimeMillis() - lastSendTime < SEND_INTERVAL_SECONDS * 1000) {
|
||||
logger.warn("邮件发送过于频繁,邮箱: {}", email);
|
||||
logger.warn("邮件发送过于频繁,邮箱: {}", normEmail);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -89,10 +98,10 @@ public class VerificationCodeService {
|
||||
String code = generateVerificationCode();
|
||||
|
||||
// 发送邮件
|
||||
boolean success = sendEmail(email, code);
|
||||
boolean success = sendEmail(normEmail, code);
|
||||
if (success) {
|
||||
// 存储验证码到内存
|
||||
String codeKey = "email_code:" + email;
|
||||
String codeKey = "email_code:" + normEmail;
|
||||
verificationCodes.put(codeKey, code);
|
||||
|
||||
// 设置发送频率限制
|
||||
@@ -101,10 +110,10 @@ public class VerificationCodeService {
|
||||
// 设置验证码过期时间
|
||||
scheduler.schedule(() -> {
|
||||
verificationCodes.remove(codeKey);
|
||||
logger.info("验证码已过期,邮箱: {}", email);
|
||||
logger.info("验证码已过期,邮箱: {}", normEmail);
|
||||
}, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||||
|
||||
logger.info("邮件验证码发送成功,邮箱: {}", email);
|
||||
logger.info("邮件验证码发送成功,邮箱: {}", normEmail);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -121,17 +130,18 @@ public class VerificationCodeService {
|
||||
*/
|
||||
public boolean verifyEmailCode(String email, String code) {
|
||||
try {
|
||||
String codeKey = "email_code:" + email;
|
||||
String normEmail = normalizeEmail(email);
|
||||
String codeKey = "email_code:" + normEmail;
|
||||
String storedCode = verificationCodes.get(codeKey);
|
||||
|
||||
|
||||
if (storedCode != null && storedCode.equals(code)) {
|
||||
// 验证成功后删除验证码
|
||||
verificationCodes.remove(codeKey);
|
||||
logger.info("邮件验证码验证成功,邮箱: {}", email);
|
||||
logger.info("邮件验证码验证成功,邮箱: {}", normEmail);
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.warn("邮件验证码验证失败,邮箱: {}, 输入码: {}", email, code);
|
||||
|
||||
logger.warn("邮件验证码验证失败,邮箱: {}, 输入码: {}", normEmail, code);
|
||||
return false;
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -140,20 +150,30 @@ public class VerificationCodeService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码(仅用于开发模式返回给前端)
|
||||
*/
|
||||
public String getVerificationCode(String email) {
|
||||
String normEmail = normalizeEmail(email);
|
||||
String codeKey = "email_code:" + normEmail;
|
||||
return verificationCodes.get(codeKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开发模式:直接设置验证码(仅开发环境使用)
|
||||
*/
|
||||
public void setVerificationCode(String email, String code) {
|
||||
String codeKey = "email_code:" + email;
|
||||
String normEmail = normalizeEmail(email);
|
||||
String codeKey = "email_code:" + normEmail;
|
||||
verificationCodes.put(codeKey, code);
|
||||
|
||||
|
||||
// 设置验证码过期时间
|
||||
scheduler.schedule(() -> {
|
||||
verificationCodes.remove(codeKey);
|
||||
logger.info("开发模式验证码已过期,邮箱: {}", email);
|
||||
logger.info("开发模式验证码已过期,邮箱: {}", normEmail);
|
||||
}, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||||
|
||||
logger.info("开发模式验证码设置成功,邮箱: {}, 验证码: {}", email, code);
|
||||
|
||||
logger.info("开发模式验证码设置成功,邮箱: {}, 验证码: {}", normEmail, code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -93,6 +93,18 @@ public class JwtUtils {
|
||||
* 获取Token中的所有声明
|
||||
*/
|
||||
private Claims getAllClaimsFromToken(String token) {
|
||||
if (token == null) {
|
||||
throw new io.jsonwebtoken.JwtException("JWT token is null");
|
||||
}
|
||||
|
||||
// 快速校验格式:紧凑形式的 JWS 应包含 2 个点(header.payload.signature)
|
||||
long dotCount = token.chars().filter(ch -> ch == '.').count();
|
||||
if (dotCount != 2) {
|
||||
throw new io.jsonwebtoken.MalformedJwtException(
|
||||
"Invalid compact JWT string: expected 2 period characters, found: " + dotCount
|
||||
);
|
||||
}
|
||||
|
||||
return Jwts.parser()
|
||||
.verifyWith(getSigningKey())
|
||||
.build()
|
||||
|
||||
@@ -16,9 +16,13 @@ spring.datasource.hikari.connection-test-query=SELECT 1
|
||||
|
||||
# JPA配置
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.show-sql=false
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=false
|
||||
|
||||
# 数据初始化配置 - 开发环境启动时自动执行 data.sql
|
||||
spring.sql.init.mode=always
|
||||
spring.sql.init.continue-on-error=true
|
||||
|
||||
# 服务器配置
|
||||
server.port=8080
|
||||
@@ -39,6 +43,10 @@ spring.servlet.multipart.enabled=true
|
||||
# 日志配置
|
||||
logging.level.com.example.demo=DEBUG
|
||||
logging.level.org.springframework.security=DEBUG
|
||||
# 关闭 Hibernate SQL 日志
|
||||
logging.level.org.hibernate.SQL=WARN
|
||||
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=WARN
|
||||
logging.level.org.hibernate.orm.jdbc.bind=WARN
|
||||
|
||||
# JWT配置
|
||||
jwt.secret=mySecretKey123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
# 生产环境配置
|
||||
spring.h2.console.enabled=false
|
||||
|
||||
# MySQL DataSource (PROD) - 使用环境变量
|
||||
spring.datasource.url=${DB_URL}
|
||||
# ============================================
|
||||
# MySQL 数据库配置 (生产环境)
|
||||
# ============================================
|
||||
# 使用环境变量配置数据库连接,适合线上服务器部署
|
||||
#
|
||||
# 部署时需要设置以下环境变量:
|
||||
# - DB_HOST: 数据库主机地址(如:127.0.0.1 或域名)
|
||||
# - DB_PORT: 数据库端口(默认:3306)
|
||||
# - DB_NAME: 数据库名称(默认:aigc_platform)
|
||||
# - DB_USERNAME: 数据库用户名
|
||||
# - DB_PASSWORD: 数据库密码
|
||||
#
|
||||
# 宝塔部署示例环境变量设置:
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_NAME=aigc_platform
|
||||
# DB_USERNAME=aigc_user
|
||||
# DB_PASSWORD=your_secure_password_here
|
||||
# ============================================
|
||||
|
||||
spring.datasource.url=jdbc:mysql://43.156.12.172:3306/aigc_platform?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.username=${DB_USERNAME}
|
||||
spring.datasource.password=${DB_PASSWORD}
|
||||
spring.datasource.username=aigc_platform
|
||||
spring.datasource.password=jRbHPZbbkdm24yTT
|
||||
|
||||
# 数据库连接池配置 (生产环境 - 支持50人并发)
|
||||
spring.datasource.hikari.maximum-pool-size=30
|
||||
@@ -17,8 +36,9 @@ spring.datasource.hikari.leak-detection-threshold=60000
|
||||
spring.datasource.hikari.validation-timeout=3000
|
||||
spring.datasource.hikari.connection-test-query=SELECT 1
|
||||
|
||||
# 强烈建议生产环境禁用自动建表
|
||||
spring.jpa.hibernate.ddl-auto=validate
|
||||
# 生产环境:使用 update 模式自动创建/更新表结构(首次部署)
|
||||
# 部署完成后可改为 validate 模式以提高安全性
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=false
|
||||
spring.jpa.properties.hibernate.format_sql=false
|
||||
|
||||
@@ -43,8 +63,9 @@ alipay.public-key=${ALIPAY_PUBLIC_KEY}
|
||||
alipay.gateway-url=https://openapi.alipay.com/gateway.do
|
||||
alipay.charset=UTF-8
|
||||
alipay.sign-type=RSA2
|
||||
alipay.notify-url=${ALIPAY_NOTIFY_URL}
|
||||
alipay.return-url=${ALIPAY_RETURN_URL}
|
||||
alipay.domain=${ALIPAY_DOMAIN:https://vionow.com}
|
||||
alipay.notify-url=${ALIPAY_NOTIFY_URL:https://vionow.com/api/payments/alipay/notify}
|
||||
alipay.return-url=${ALIPAY_RETURN_URL:https://vionow.com/payment/success}
|
||||
|
||||
# JWT配置 - 使用环境变量
|
||||
jwt.secret=${JWT_SECRET}
|
||||
@@ -71,6 +92,10 @@ server.tomcat.connection-timeout=20000
|
||||
logging.level.root=INFO
|
||||
logging.level.com.example.demo=INFO
|
||||
logging.level.org.springframework.security=WARN
|
||||
# 关闭 Hibernate SQL 日志
|
||||
logging.level.org.hibernate.SQL=WARN
|
||||
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=WARN
|
||||
logging.level.org.hibernate.orm.jdbc.bind=WARN
|
||||
logging.file.name=${LOG_FILE_PATH:./logs/application.log}
|
||||
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
|
||||
|
||||
@@ -88,6 +113,18 @@ app.ffmpeg.path=${FFMPEG_PATH:ffmpeg}
|
||||
# 注意:确保应用有读写权限
|
||||
app.upload.path=${UPLOAD_PATH:./uploads}
|
||||
|
||||
# SpringDoc OpenAPI (Swagger) 配置
|
||||
# 生产环境建议禁用或限制访问
|
||||
springdoc.api-docs.path=/v3/api-docs
|
||||
springdoc.swagger-ui.path=/swagger-ui.html
|
||||
springdoc.swagger-ui.enabled=true
|
||||
springdoc.swagger-ui.operationsSorter=method
|
||||
springdoc.swagger-ui.tagsSorter=alpha
|
||||
springdoc.swagger-ui.tryItOutEnabled=true
|
||||
springdoc.swagger-ui.filter=true
|
||||
springdoc.swagger-ui.display-request-duration=true
|
||||
springdoc.swagger-ui.doc-expansion=none
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ spring.thymeleaf.cache=false
|
||||
spring.profiles.active=dev
|
||||
|
||||
# 服务器配置
|
||||
server.address=localhost
|
||||
server.address=0.0.0.0
|
||||
server.port=8080
|
||||
|
||||
# 文件上传配置(扩大请求体大小以支持大图片Base64编码)
|
||||
@@ -25,3 +25,15 @@ jwt.expiration=86400000
|
||||
# AI API配置
|
||||
ai.api.base-url=http://116.62.4.26:8081
|
||||
ai.api.key=ak_5f13ec469e6047d5b8155c3cc91350e2
|
||||
|
||||
# SpringDoc OpenAPI (Swagger) 配置
|
||||
springdoc.api-docs.path=/v3/api-docs
|
||||
springdoc.swagger-ui.path=/swagger-ui.html
|
||||
springdoc.swagger-ui.operationsSorter=method
|
||||
springdoc.swagger-ui.tagsSorter=alpha
|
||||
springdoc.swagger-ui.tryItOutEnabled=true
|
||||
springdoc.swagger-ui.filter=true
|
||||
springdoc.swagger-ui.display-request-duration=true
|
||||
springdoc.swagger-ui.doc-expansion=none
|
||||
springdoc.swagger-ui.default-models-expand-depth=1
|
||||
springdoc.swagger-ui.default-model-expand-depth=1
|
||||
|
||||
@@ -1,123 +1,15 @@
|
||||
-- 用户数据
|
||||
INSERT IGNORE INTO users (username, email, password_hash, role, points, phone, nickname, gender, birthday, address) VALUES
|
||||
('admin', 'admin@example.com', 'admin123', 'ROLE_ADMIN', 200, '15538239326', '管理员', 'M', '1990-01-01', '北京市朝阳区'),
|
||||
('13689270819', '13689270819@example.com', '0627', 'ROLE_USER', 100, '13689270819', '演示用户', 'M', '1995-05-15', '上海市浦东新区'),
|
||||
('testuser', 'testuser@example.com', 'test123', 'ROLE_USER', 75, '13900139000', '测试用户', 'F', '1992-08-20', '广州市天河区'),
|
||||
('mingzi_FBx7foZYDS7inLQb', 'mingzi@example.com', '123456', 'ROLE_USER', 25, '13700137000', '名字用户', 'M', '1988-12-10', '深圳市南山区'),
|
||||
('user001', 'user001@example.com', 'password123', 'ROLE_USER', 150, '13600136000', '用户001', 'M', '1991-07-12', '成都市锦江区'),
|
||||
('user002', 'user002@example.com', 'password123', 'ROLE_USER', 80, '13500135000', '用户002', 'F', '1994-11-08', '武汉市江汉区'),
|
||||
('user003', 'user003@example.com', 'password123', 'ROLE_USER', 200, '13400134000', '用户003', 'M', '1989-04-18', '西安市雁塔区'),
|
||||
('user004', 'user004@example.com', 'password123', 'ROLE_USER', 120, '13300133000', '用户004', 'F', '1996-09-30', '南京市鼓楼区'),
|
||||
('user005', 'user005@example.com', 'password123', 'ROLE_USER', 90, '13200132000', '用户005', 'M', '1990-06-22', '重庆市渝中区');
|
||||
-- 初始化数据文件
|
||||
-- 此文件用于开发/测试环境的初始数据
|
||||
-- 生产环境请勿使用此文件,应通过系统管理界面或API进行数据初始化
|
||||
|
||||
-- 会员等级数据
|
||||
INSERT IGNORE INTO membership_levels (name, display_name, description, price, duration_days, points_bonus, features) VALUES
|
||||
('standard', '标准会员', '基础会员服务,包含基本功能', 29.00, 30, 50, '{"video_quality": "720p", "storage": "5GB", "support": "email"}'),
|
||||
('professional', '专业会员', '专业会员服务,包含高级功能', 99.00, 30, 200, '{"video_quality": "1080p", "storage": "20GB", "support": "priority", "api_access": true}'),
|
||||
('enterprise', '企业会员', '企业级服务,包含所有功能', 299.00, 30, 500, '{"video_quality": "4K", "storage": "100GB", "support": "dedicated", "api_access": true, "custom_branding": true}');
|
||||
-- 注意:生产环境部署时,此文件应保持为空或仅包含必要的系统配置数据
|
||||
|
||||
-- 用户会员信息
|
||||
INSERT IGNORE INTO user_memberships (user_id, membership_level_id, start_date, end_date, status, auto_renew) VALUES
|
||||
(1, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true),
|
||||
(2, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false),
|
||||
(3, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false),
|
||||
(4, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true),
|
||||
(5, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true),
|
||||
(6, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false),
|
||||
(7, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false),
|
||||
(8, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true),
|
||||
(9, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false),
|
||||
(10, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false);
|
||||
|
||||
-- 订单数据
|
||||
INSERT IGNORE INTO orders (order_number, total_amount, currency, status, order_type, description, user_id, created_at) VALUES
|
||||
('ORD20240101001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-01-01 10:00:00'),
|
||||
('ORD20240101002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-01-01 11:00:00'),
|
||||
('ORD20240101003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-01-01 12:00:00'),
|
||||
('ORD20240101004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-01-01 13:00:00'),
|
||||
('ORD20240101005', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 5, '2024-01-01 14:00:00'),
|
||||
('ORD20240101006', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-01-01 15:00:00'),
|
||||
('ORD20240101007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-01-01 16:00:00'),
|
||||
('ORD20240101008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 8, '2024-01-01 17:00:00'),
|
||||
('ORD20240101009', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 9, '2024-01-01 18:00:00'),
|
||||
('ORD20240101010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-01-01 19:00:00'),
|
||||
('ORD20240102001', 199.00, 'CNY', 'PENDING', 'PRODUCT', '视频生成服务包', 1, '2024-01-02 09:00:00'),
|
||||
('ORD20240102002', 99.00, 'CNY', 'PROCESSING', 'PRODUCT', '高级视频编辑', 2, '2024-01-02 10:00:00'),
|
||||
('ORD20240102003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 3, '2024-01-02 11:00:00'),
|
||||
('ORD20240102004', 49.00, 'CNY', 'CANCELLED', 'PRODUCT', '基础视频生成', 4, '2024-01-02 12:00:00'),
|
||||
('ORD20240102005', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-01-02 13:00:00');
|
||||
|
||||
-- 订单商品数据
|
||||
INSERT IGNORE INTO order_items (product_name, product_description, unit_price, quantity, subtotal, order_id) VALUES
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 1),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 2),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 3),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 4),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 5),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 6),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 7),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 8),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 9),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 10),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 11),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 12),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 13),
|
||||
('基础视频生成', '单次视频生成服务', 49.00, 1, 49.00, 14),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 15);
|
||||
|
||||
-- 支付数据
|
||||
INSERT IGNORE INTO payments (order_id, amount, currency, payment_method, status, description, user_id, created_at, paid_at) VALUES
|
||||
('ORD20240101001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 1, '2024-01-01 10:00:00', '2024-01-01 10:05:00'),
|
||||
('ORD20240101002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 2, '2024-01-01 11:00:00', '2024-01-01 11:02:00'),
|
||||
('ORD20240101003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 3, '2024-01-01 12:00:00', '2024-01-01 12:03:00'),
|
||||
('ORD20240101004', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 4, '2024-01-01 13:00:00', '2024-01-01 13:04:00'),
|
||||
('ORD20240101005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 5, '2024-01-01 14:00:00', '2024-01-01 14:05:00'),
|
||||
('ORD20240101006', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 6, '2024-01-01 15:00:00', '2024-01-01 15:02:00'),
|
||||
('ORD20240101007', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 7, '2024-01-01 16:00:00', '2024-01-01 16:03:00'),
|
||||
('ORD20240101008', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 8, '2024-01-01 17:00:00', '2024-01-01 17:04:00'),
|
||||
('ORD20240101009', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 9, '2024-01-01 18:00:00', '2024-01-01 18:02:00'),
|
||||
('ORD20240101010', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 10, '2024-01-01 19:00:00', '2024-01-01 19:03:00'),
|
||||
('ORD20240102001', 199.00, 'CNY', 'ALIPAY', 'PENDING', '视频生成服务包', 1, '2024-01-02 09:00:00', NULL),
|
||||
('ORD20240102002', 99.00, 'CNY', 'WECHAT', 'PROCESSING', '高级视频编辑', 2, '2024-01-02 10:00:00', NULL),
|
||||
('ORD20240102003', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 3, '2024-01-02 11:00:00', '2024-01-02 11:05:00'),
|
||||
('ORD20240102004', 49.00, 'CNY', 'WECHAT', 'CANCELLED', '基础视频生成', 4, '2024-01-02 12:00:00', NULL),
|
||||
('ORD20240102005', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 5, '2024-01-02 13:00:00', '2024-01-02 13:04:00');
|
||||
|
||||
-- 视频生成任务数据
|
||||
INSERT IGNORE INTO video_tasks (task_id, user_id, task_type, title, description, input_text, status, progress, created_at, completed_at) VALUES
|
||||
('TASK20240101001', 1, 'TEXT_TO_VIDEO', '产品介绍视频', '为公司新产品制作的介绍视频', '这是一款革命性的AI产品,能够帮助用户快速生成高质量的视频内容...', 'COMPLETED', 100, '2024-01-01 10:00:00', '2024-01-01 10:30:00'),
|
||||
('TASK20240101002', 2, 'IMAGE_TO_VIDEO', '风景动画', '将静态风景图片转换为动态视频', NULL, 'COMPLETED', 100, '2024-01-01 11:00:00', '2024-01-01 11:25:00'),
|
||||
('TASK20240101003', 3, 'STORYBOARD_VIDEO', '故事板视频', '基于故事板创建的视频', '从前有一个小村庄,村民们过着平静的生活...', 'PROCESSING', 75, '2024-01-01 12:00:00', NULL),
|
||||
('TASK20240101004', 4, 'TEXT_TO_VIDEO', '教育视频', '在线教育课程视频', '今天我们来学习Vue.js的基础知识...', 'COMPLETED', 100, '2024-01-01 13:00:00', '2024-01-01 13:35:00'),
|
||||
('TASK20240101005', 5, 'IMAGE_TO_VIDEO', '产品展示', '产品图片转视频展示', NULL, 'COMPLETED', 100, '2024-01-01 14:00:00', '2024-01-01 14:20:00'),
|
||||
('TASK20240101006', 6, 'TEXT_TO_VIDEO', '营销视频', '产品营销推广视频', '限时优惠!现在购买享受8折优惠...', 'PENDING', 0, '2024-01-01 15:00:00', NULL),
|
||||
('TASK20240101007', 7, 'STORYBOARD_VIDEO', '动画短片', '创意动画短片制作', '在一个遥远的星球上,住着一群可爱的小精灵...', 'COMPLETED', 100, '2024-01-01 16:00:00', '2024-01-01 16:45:00'),
|
||||
('TASK20240101008', 8, 'TEXT_TO_VIDEO', '技术分享', '技术分享会视频', '今天分享的主题是微服务架构的设计原则...', 'PROCESSING', 60, '2024-01-01 17:00:00', NULL),
|
||||
('TASK20240101009', 9, 'IMAGE_TO_VIDEO', '艺术创作', '艺术作品动态展示', NULL, 'COMPLETED', 100, '2024-01-01 18:00:00', '2024-01-01 18:15:00'),
|
||||
('TASK20240101010', 10, 'TEXT_TO_VIDEO', '新闻播报', '新闻播报视频', '今日要闻:科技公司发布最新AI技术...', 'FAILED', 0, '2024-01-01 19:00:00', NULL);
|
||||
|
||||
-- 用户作品数据
|
||||
INSERT IGNORE INTO user_works (user_id, title, description, work_type, cover_image, video_url, tags, view_count, like_count, created_at) VALUES
|
||||
(1, '产品介绍视频', '为公司新产品制作的介绍视频', 'VIDEO', '/images/covers/product_intro.jpg', '/videos/product_intro.mp4', '产品,介绍,商业', 1250, 89, '2024-01-01 10:30:00'),
|
||||
(2, '风景动画', '将静态风景图片转换为动态视频', 'VIDEO', '/images/covers/landscape.jpg', '/videos/landscape.mp4', '风景,动画,自然', 890, 67, '2024-01-01 11:25:00'),
|
||||
(3, '故事板视频', '基于故事板创建的视频', 'VIDEO', '/images/covers/storyboard.jpg', '/videos/storyboard.mp4', '故事,创意,动画', 2100, 156, '2024-01-01 12:30:00'),
|
||||
(4, '教育视频', '在线教育课程视频', 'VIDEO', '/images/covers/education.jpg', '/videos/education.mp4', '教育,课程,学习', 3200, 234, '2024-01-01 13:35:00'),
|
||||
(5, '产品展示', '产品图片转视频展示', 'VIDEO', '/images/covers/product_show.jpg', '/videos/product_show.mp4', '产品,展示,商业', 1560, 112, '2024-01-01 14:20:00'),
|
||||
(6, '营销视频', '产品营销推广视频', 'VIDEO', '/images/covers/marketing.jpg', '/videos/marketing.mp4', '营销,推广,商业', 2800, 198, '2024-01-01 15:30:00'),
|
||||
(7, '动画短片', '创意动画短片制作', 'VIDEO', '/images/covers/animation.jpg', '/videos/animation.mp4', '动画,创意,短片', 4500, 345, '2024-01-01 16:45:00'),
|
||||
(8, '技术分享', '技术分享会视频', 'VIDEO', '/images/covers/tech_share.jpg', '/videos/tech_share.mp4', '技术,分享,编程', 1800, 134, '2024-01-01 17:30:00'),
|
||||
(9, '艺术创作', '艺术作品动态展示', 'VIDEO', '/images/covers/art.jpg', '/videos/art.mp4', '艺术,创作,美学', 950, 78, '2024-01-01 18:15:00'),
|
||||
(10, '新闻播报', '新闻播报视频', 'VIDEO', '/images/covers/news.jpg', '/videos/news.mp4', '新闻,播报,资讯', 1200, 89, '2024-01-01 19:30:00');
|
||||
|
||||
-- 系统配置数据
|
||||
INSERT IGNORE INTO system_configs (config_key, config_value, description, config_type, is_public) VALUES
|
||||
('site_name', 'AIGC视频生成平台', '网站名称', 'STRING', true),
|
||||
('site_description', '专业的AI视频生成服务平台', '网站描述', 'STRING', true),
|
||||
('max_file_size', '100', '最大文件上传大小(MB)', 'NUMBER', false),
|
||||
('supported_formats', '["mp4", "avi", "mov", "wmv"]', '支持的视频格式', 'JSON', true),
|
||||
('default_video_quality', '1080p', '默认视频质量', 'STRING', false),
|
||||
('max_video_duration', '300', '最大视频时长(秒)', 'NUMBER', false),
|
||||
('api_rate_limit', '100', 'API调用频率限制(次/小时)', 'NUMBER', false),
|
||||
('maintenance_mode', 'false', '维护模式开关', 'BOOLEAN', false),
|
||||
('registration_enabled', 'true', '用户注册开关', 'BOOLEAN', true),
|
||||
('email_verification', 'false', '邮箱验证开关', 'BOOLEAN', false);
|
||||
-- ============================================
|
||||
-- 管理员权限自动设置
|
||||
-- ============================================
|
||||
-- 应用启动时自动将 984523799@qq.com 设置为管理员
|
||||
-- 如果该用户存在,则更新其角色为管理员
|
||||
UPDATE users
|
||||
SET role = 'ROLE_ADMIN',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = '984523799@qq.com';
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 添加 STORYBOARD_IMAGE 到 user_works 表的 work_type 枚举
|
||||
|
||||
ALTER TABLE user_works
|
||||
MODIFY COLUMN work_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO', 'STORYBOARD_VIDEO', 'STORYBOARD_IMAGE') NOT NULL COMMENT '作品类型';
|
||||
@@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS task_queue (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(100) NOT NULL COMMENT '用户名',
|
||||
task_id VARCHAR(50) NOT NULL UNIQUE COMMENT '任务ID',
|
||||
task_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO') NOT NULL COMMENT '任务类型',
|
||||
task_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO', 'STORYBOARD_VIDEO') NOT NULL COMMENT '任务类型',
|
||||
status ENUM('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED', 'CANCELLED', 'TIMEOUT') NOT NULL DEFAULT 'PENDING' COMMENT '队列状态',
|
||||
priority INT NOT NULL DEFAULT 0 COMMENT '优先级,数字越小优先级越高',
|
||||
real_task_id VARCHAR(100) COMMENT '外部API返回的真实任务ID',
|
||||
@@ -37,3 +37,6 @@ CREATE TABLE IF NOT EXISTS task_queue (
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS points_freeze_records (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(100) NOT NULL COMMENT '用户名',
|
||||
task_id VARCHAR(50) NOT NULL UNIQUE COMMENT '任务ID',
|
||||
task_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO') NOT NULL COMMENT '任务类型',
|
||||
task_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO', 'STORYBOARD_VIDEO') NOT NULL COMMENT '任务类型',
|
||||
freeze_points INT NOT NULL COMMENT '冻结的积分数量',
|
||||
status ENUM('FROZEN', 'DEDUCTED', 'RETURNED', 'EXPIRED') NOT NULL DEFAULT 'FROZEN' COMMENT '冻结状态',
|
||||
freeze_reason VARCHAR(200) COMMENT '冻结原因',
|
||||
@@ -36,3 +36,6 @@ CREATE TABLE IF NOT EXISTS points_freeze_records (
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS user_works (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(100) NOT NULL COMMENT '用户名',
|
||||
task_id VARCHAR(50) NOT NULL UNIQUE COMMENT '任务ID',
|
||||
work_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO') NOT NULL COMMENT '作品类型',
|
||||
work_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO', 'STORYBOARD_VIDEO') NOT NULL COMMENT '作品类型',
|
||||
title VARCHAR(200) COMMENT '作品标题',
|
||||
description TEXT COMMENT '作品描述',
|
||||
prompt TEXT COMMENT '生成提示词',
|
||||
|
||||
@@ -38,4 +38,8 @@ CREATE TABLE task_status (
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- V7: 为 task_status 表添加复合索引以加速轮询查询
|
||||
ALTER TABLE task_status
|
||||
ADD INDEX idx_status_lastpolled_pollcount (status, last_polled_at, poll_count);
|
||||
|
||||
-- 说明:
|
||||
-- 该索引用于加速下面这类查询:
|
||||
-- WHERE status = 'PROCESSING' AND poll_count < max_polls AND (last_polled_at IS NULL OR last_polled_at < :cutoffTime)
|
||||
-- 索引列顺序选择(status, last_polled_at, poll_count)能让数据库在先按 status 过滤,再按 last_polled_at 范围筛选,最后用 poll_count 进一步过滤,从而减少全表扫描。
|
||||
@@ -0,0 +1,13 @@
|
||||
-- 更新任务队列表,添加 STORYBOARD_VIDEO 类型
|
||||
-- 注意:MySQL 不支持直接修改 ENUM,需要先修改为 VARCHAR,再改回 ENUM
|
||||
ALTER TABLE task_queue
|
||||
MODIFY COLUMN task_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO', 'STORYBOARD_VIDEO') NOT NULL COMMENT '任务类型';
|
||||
|
||||
-- 更新积分冻结记录表,添加 STORYBOARD_VIDEO 类型
|
||||
ALTER TABLE points_freeze_records
|
||||
MODIFY COLUMN task_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO', 'STORYBOARD_VIDEO') NOT NULL COMMENT '任务类型';
|
||||
|
||||
-- 更新用户作品表,添加 STORYBOARD_VIDEO 类型
|
||||
ALTER TABLE user_works
|
||||
MODIFY COLUMN work_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO', 'STORYBOARD_VIDEO') NOT NULL COMMENT '作品类型';
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
-- 数据库迁移脚本:为users表添加created_at字段
|
||||
-- 如果users表不存在created_at字段,则添加它
|
||||
|
||||
-- 检查字段是否存在,如果不存在则添加
|
||||
SET @sql = IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'users'
|
||||
AND COLUMN_NAME = 'created_at') = 0,
|
||||
'ALTER TABLE users ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP',
|
||||
'SELECT "created_at column already exists" as message'
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 为现有用户设置创建时间(如果为NULL)
|
||||
UPDATE users SET created_at = CURRENT_TIMESTAMP WHERE created_at IS NULL;
|
||||
@@ -1,34 +0,0 @@
|
||||
-- 创建图生视频任务表
|
||||
CREATE TABLE IF NOT EXISTS image_to_video_tasks (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
task_id VARCHAR(50) NOT NULL UNIQUE,
|
||||
username VARCHAR(100) NOT NULL,
|
||||
first_frame_url VARCHAR(500) NOT NULL,
|
||||
last_frame_url VARCHAR(500),
|
||||
prompt TEXT,
|
||||
aspect_ratio VARCHAR(10) NOT NULL DEFAULT '16:9',
|
||||
duration INT NOT NULL DEFAULT 5,
|
||||
hd_mode BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||||
progress INT DEFAULT 0,
|
||||
result_url VARCHAR(500),
|
||||
real_task_id VARCHAR(100),
|
||||
error_message TEXT,
|
||||
cost_points INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMP NULL,
|
||||
|
||||
INDEX idx_username (username),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_created_at (created_at),
|
||||
INDEX idx_task_id (task_id)
|
||||
);
|
||||
|
||||
-- 注意:MySQL的CHECK约束支持有限,以下约束在应用层进行验证
|
||||
-- 任务状态应该在应用层验证:PENDING, PROCESSING, COMPLETED, FAILED, CANCELLED
|
||||
-- 时长应该在应用层验证:1-60秒
|
||||
-- 进度应该在应用层验证:0-100
|
||||
|
||||
-- 如果需要数据库层约束,可以使用触发器或存储过程
|
||||
-- 这里我们依赖应用层的验证逻辑
|
||||
@@ -1,32 +0,0 @@
|
||||
-- 创建文生视频任务表
|
||||
CREATE TABLE IF NOT EXISTS text_to_video_tasks (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
task_id VARCHAR(50) NOT NULL UNIQUE,
|
||||
username VARCHAR(100) NOT NULL,
|
||||
prompt TEXT,
|
||||
aspect_ratio VARCHAR(10) NOT NULL DEFAULT '16:9',
|
||||
duration INT NOT NULL DEFAULT 5,
|
||||
hd_mode BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||||
progress INT DEFAULT 0,
|
||||
result_url VARCHAR(500),
|
||||
real_task_id VARCHAR(100),
|
||||
error_message TEXT,
|
||||
cost_points INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMP NULL,
|
||||
|
||||
INDEX idx_username (username),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_created_at (created_at),
|
||||
INDEX idx_task_id (task_id)
|
||||
);
|
||||
|
||||
-- 注意:MySQL的CHECK约束支持有限,以下约束在应用层进行验证
|
||||
-- 任务状态应该在应用层验证:PENDING, PROCESSING, COMPLETED, FAILED, CANCELLED
|
||||
-- 时长应该在应用层验证:1-60秒
|
||||
-- 进度应该在应用层验证:0-100
|
||||
|
||||
-- 如果需要数据库层约束,可以使用触发器或存储过程
|
||||
-- 这里我们依赖应用层的验证逻辑
|
||||
@@ -1,207 +0,0 @@
|
||||
-- 12个月订单商品数据
|
||||
-- 对应orders_12months.sql中的订单
|
||||
|
||||
INSERT IGNORE INTO order_items (product_name, product_description, unit_price, quantity, subtotal, order_id) VALUES
|
||||
-- 2024年1月订单商品
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 1),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 2),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 3),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 4),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 5),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 6),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 7),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 8),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 9),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 10),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 11),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 12),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 13),
|
||||
('基础视频生成', '单次视频生成服务', 49.00, 1, 49.00, 14),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 15),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 16),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 17),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 18),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 19),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 20),
|
||||
|
||||
-- 2024年2月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 21),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 22),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 23),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 24),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 25),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 26),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 27),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 28),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 29),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 30),
|
||||
|
||||
-- 2024年3月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 31),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 32),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 33),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 34),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 35),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 36),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 37),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 38),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 39),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 40),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 41),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 42),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 43),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 44),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 45),
|
||||
|
||||
-- 2024年4月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 46),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 47),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 48),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 49),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 50),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 51),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 52),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 53),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 54),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 55),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 56),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 57),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 58),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 59),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 60),
|
||||
|
||||
-- 2024年5月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 61),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 62),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 63),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 64),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 65),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 66),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 67),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 68),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 69),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 70),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 71),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 72),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 73),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 74),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 75),
|
||||
|
||||
-- 2024年6月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 76),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 77),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 78),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 79),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 80),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 81),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 82),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 83),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 84),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 85),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 86),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 87),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 88),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 89),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 90),
|
||||
|
||||
-- 2024年7月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 91),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 92),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 93),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 94),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 95),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 96),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 97),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 98),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 99),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 100),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 101),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 102),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 103),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 104),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 105),
|
||||
|
||||
-- 2024年8月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 106),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 107),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 108),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 109),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 110),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 111),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 112),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 113),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 114),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 115),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 116),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 117),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 118),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 119),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 120),
|
||||
|
||||
-- 2024年9月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 121),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 122),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 123),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 124),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 125),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 126),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 127),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 128),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 129),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 130),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 131),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 132),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 133),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 134),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 135),
|
||||
|
||||
-- 2024年10月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 136),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 137),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 138),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 139),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 140),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 141),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 142),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 143),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 144),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 145),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 146),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 147),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 148),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 149),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 150),
|
||||
|
||||
-- 2024年11月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 151),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 152),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 153),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 154),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 155),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 156),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 157),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 158),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 159),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 160),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 161),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 162),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 163),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 164),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 165),
|
||||
|
||||
-- 2024年12月订单商品
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 166),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 167),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 168),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 169),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 170),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 171),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 172),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 173),
|
||||
('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 174),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 175),
|
||||
('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 176),
|
||||
('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 177),
|
||||
('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 178),
|
||||
('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 179),
|
||||
('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 180);
|
||||
@@ -1,25 +0,0 @@
|
||||
-- 插入订单项数据
|
||||
INSERT INTO order_items (order_id, product_name, quantity, unit_price, subtotal)
|
||||
SELECT
|
||||
o.id as order_id,
|
||||
CASE
|
||||
WHEN o.order_type = 'MEMBERSHIP' THEN
|
||||
CASE
|
||||
WHEN o.total_amount = 29.00 THEN '标准会员订阅'
|
||||
WHEN o.total_amount = 99.00 THEN '专业会员订阅'
|
||||
WHEN o.total_amount = 299.00 THEN '企业会员订阅'
|
||||
ELSE '会员订阅'
|
||||
END
|
||||
WHEN o.order_type = 'PRODUCT' THEN
|
||||
CASE
|
||||
WHEN o.total_amount = 49.00 THEN '基础视频生成'
|
||||
WHEN o.total_amount = 99.00 THEN '高级视频编辑'
|
||||
WHEN o.total_amount = 199.00 THEN '视频生成服务包'
|
||||
ELSE '视频服务'
|
||||
END
|
||||
ELSE '其他服务'
|
||||
END as product_name,
|
||||
1 as quantity,
|
||||
o.total_amount as unit_price,
|
||||
o.total_amount as subtotal
|
||||
FROM orders o;
|
||||
@@ -1,208 +0,0 @@
|
||||
-- 12个月订单数据生成脚本
|
||||
-- 生成2024年1月到12月的订单数据
|
||||
|
||||
-- 订单数据 (2024年1月-12月)
|
||||
INSERT IGNORE INTO orders (order_number, total_amount, currency, status, order_type, description, user_id, created_at) VALUES
|
||||
-- 2024年1月订单
|
||||
('ORD20240101001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-01-01 10:00:00'),
|
||||
('ORD20240101002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-01-01 11:00:00'),
|
||||
('ORD20240101003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-01-01 12:00:00'),
|
||||
('ORD20240101004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-01-01 13:00:00'),
|
||||
('ORD20240101005', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 5, '2024-01-01 14:00:00'),
|
||||
('ORD20240101006', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-01-01 15:00:00'),
|
||||
('ORD20240101007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-01-01 16:00:00'),
|
||||
('ORD20240101008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 8, '2024-01-01 17:00:00'),
|
||||
('ORD20240101009', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 9, '2024-01-01 18:00:00'),
|
||||
('ORD20240101010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-01-01 19:00:00'),
|
||||
('ORD20240102001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 1, '2024-01-02 09:00:00'),
|
||||
('ORD20240102002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 2, '2024-01-02 10:00:00'),
|
||||
('ORD20240102003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 3, '2024-01-02 11:00:00'),
|
||||
('ORD20240102004', 49.00, 'CNY', 'CANCELLED', 'PRODUCT', '基础视频生成', 4, '2024-01-02 12:00:00'),
|
||||
('ORD20240102005', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-01-02 13:00:00'),
|
||||
('ORD20240115001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-01-15 14:30:00'),
|
||||
('ORD20240115002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 7, '2024-01-15 15:45:00'),
|
||||
('ORD20240115003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-01-15 16:20:00'),
|
||||
('ORD20240128001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 9, '2024-01-28 10:15:00'),
|
||||
('ORD20240128002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-01-28 11:30:00'),
|
||||
|
||||
-- 2024年2月订单
|
||||
('ORD20240201001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-02-01 09:00:00'),
|
||||
('ORD20240201002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 2, '2024-02-01 10:15:00'),
|
||||
('ORD20240201003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-02-01 11:30:00'),
|
||||
('ORD20240201004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 4, '2024-02-01 14:20:00'),
|
||||
('ORD20240201005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 5, '2024-02-01 15:45:00'),
|
||||
('ORD20240214001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 6, '2024-02-14 10:00:00'),
|
||||
('ORD20240214002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-02-14 11:30:00'),
|
||||
('ORD20240214003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-02-14 14:15:00'),
|
||||
('ORD20240225001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-02-25 16:00:00'),
|
||||
('ORD20240225002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-02-25 17:30:00'),
|
||||
|
||||
-- 2024年3月订单
|
||||
('ORD20240301001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-03-01 09:30:00'),
|
||||
('ORD20240301002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 2, '2024-03-01 10:45:00'),
|
||||
('ORD20240301003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-03-01 11:20:00'),
|
||||
('ORD20240301004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 4, '2024-03-01 14:10:00'),
|
||||
('ORD20240301005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 5, '2024-03-01 15:25:00'),
|
||||
('ORD20240308001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 6, '2024-03-08 10:00:00'),
|
||||
('ORD20240308002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-03-08 11:15:00'),
|
||||
('ORD20240308003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-03-08 14:30:00'),
|
||||
('ORD20240315001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-03-15 16:45:00'),
|
||||
('ORD20240315002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-03-15 17:20:00'),
|
||||
('ORD20240322001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-03-22 09:15:00'),
|
||||
('ORD20240322002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-03-22 10:30:00'),
|
||||
('ORD20240322003', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 3, '2024-03-22 14:45:00'),
|
||||
('ORD20240330001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 4, '2024-03-30 11:00:00'),
|
||||
('ORD20240330002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-03-30 15:30:00'),
|
||||
|
||||
-- 2024年4月订单
|
||||
('ORD20240401001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-04-01 09:00:00'),
|
||||
('ORD20240401002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-04-01 10:20:00'),
|
||||
('ORD20240401003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-04-01 11:40:00'),
|
||||
('ORD20240401004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 9, '2024-04-01 14:15:00'),
|
||||
('ORD20240401005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 10, '2024-04-01 15:35:00'),
|
||||
('ORD20240410001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 1, '2024-04-10 10:30:00'),
|
||||
('ORD20240410002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-04-10 11:45:00'),
|
||||
('ORD20240410003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-04-10 14:20:00'),
|
||||
('ORD20240420001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-04-20 16:10:00'),
|
||||
('ORD20240420002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-04-20 17:25:00'),
|
||||
('ORD20240420003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-04-20 18:40:00'),
|
||||
('ORD20240425001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 7, '2024-04-25 09:15:00'),
|
||||
('ORD20240425002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 8, '2024-04-25 10:30:00'),
|
||||
('ORD20240425003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 9, '2024-04-25 14:45:00'),
|
||||
('ORD20240430001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-04-30 15:20:00'),
|
||||
|
||||
-- 2024年5月订单
|
||||
('ORD20240501001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-05-01 09:30:00'),
|
||||
('ORD20240501002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 2, '2024-05-01 10:45:00'),
|
||||
('ORD20240501003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-05-01 11:20:00'),
|
||||
('ORD20240501004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 4, '2024-05-01 14:10:00'),
|
||||
('ORD20240501005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 5, '2024-05-01 15:25:00'),
|
||||
('ORD20240510001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 6, '2024-05-10 10:00:00'),
|
||||
('ORD20240510002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-05-10 11:15:00'),
|
||||
('ORD20240510003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-05-10 14:30:00'),
|
||||
('ORD20240520001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-05-20 16:45:00'),
|
||||
('ORD20240520002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-05-20 17:20:00'),
|
||||
('ORD20240520003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-05-20 18:35:00'),
|
||||
('ORD20240525001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-05-25 09:50:00'),
|
||||
('ORD20240525002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 3, '2024-05-25 11:05:00'),
|
||||
('ORD20240525003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 4, '2024-05-25 14:20:00'),
|
||||
('ORD20240530001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-05-30 15:35:00'),
|
||||
|
||||
-- 2024年6月订单
|
||||
('ORD20240601001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-06-01 09:15:00'),
|
||||
('ORD20240601002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-06-01 10:30:00'),
|
||||
('ORD20240601003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-06-01 11:45:00'),
|
||||
('ORD20240601004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 9, '2024-06-01 14:00:00'),
|
||||
('ORD20240601005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 10, '2024-06-01 15:15:00'),
|
||||
('ORD20240615001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 1, '2024-06-15 10:45:00'),
|
||||
('ORD20240615002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-06-15 12:00:00'),
|
||||
('ORD20240615003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-06-15 14:15:00'),
|
||||
('ORD20240620001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-06-20 16:30:00'),
|
||||
('ORD20240620002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-06-20 17:45:00'),
|
||||
('ORD20240620003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-06-20 19:00:00'),
|
||||
('ORD20240625001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 7, '2024-06-25 09:30:00'),
|
||||
('ORD20240625002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 8, '2024-06-25 10:45:00'),
|
||||
('ORD20240625003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 9, '2024-06-25 14:00:00'),
|
||||
('ORD20240630001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-06-30 15:15:00'),
|
||||
|
||||
-- 2024年7月订单
|
||||
('ORD20240701001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-07-01 09:45:00'),
|
||||
('ORD20240701002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 2, '2024-07-01 11:00:00'),
|
||||
('ORD20240701003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-07-01 12:15:00'),
|
||||
('ORD20240701004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 4, '2024-07-01 14:30:00'),
|
||||
('ORD20240701005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 5, '2024-07-01 15:45:00'),
|
||||
('ORD20240710001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 6, '2024-07-10 10:20:00'),
|
||||
('ORD20240710002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-07-10 11:35:00'),
|
||||
('ORD20240710003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-07-10 14:50:00'),
|
||||
('ORD20240720001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-07-20 16:05:00'),
|
||||
('ORD20240720002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-07-20 17:20:00'),
|
||||
('ORD20240720003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-07-20 18:35:00'),
|
||||
('ORD20240725001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-07-25 09:50:00'),
|
||||
('ORD20240725002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 3, '2024-07-25 11:05:00'),
|
||||
('ORD20240725003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 4, '2024-07-25 14:20:00'),
|
||||
('ORD20240730001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-07-30 15:35:00'),
|
||||
|
||||
-- 2024年8月订单
|
||||
('ORD20240801001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-08-01 09:30:00'),
|
||||
('ORD20240801002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-08-01 10:45:00'),
|
||||
('ORD20240801003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-08-01 12:00:00'),
|
||||
('ORD20240801004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 9, '2024-08-01 14:15:00'),
|
||||
('ORD20240801005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 10, '2024-08-01 15:30:00'),
|
||||
('ORD20240810001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 1, '2024-08-10 10:00:00'),
|
||||
('ORD20240810002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-08-10 11:15:00'),
|
||||
('ORD20240810003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-08-10 14:30:00'),
|
||||
('ORD20240820001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-08-20 16:45:00'),
|
||||
('ORD20240820002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-08-20 18:00:00'),
|
||||
('ORD20240820003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-08-20 19:15:00'),
|
||||
('ORD20240825001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 7, '2024-08-25 09:30:00'),
|
||||
('ORD20240825002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 8, '2024-08-25 10:45:00'),
|
||||
('ORD20240825003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 9, '2024-08-25 14:00:00'),
|
||||
('ORD20240830001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-08-30 15:15:00'),
|
||||
|
||||
-- 2024年9月订单
|
||||
('ORD20240901001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-09-01 09:15:00'),
|
||||
('ORD20240901002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 2, '2024-09-01 10:30:00'),
|
||||
('ORD20240901003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-09-01 11:45:00'),
|
||||
('ORD20240901004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 4, '2024-09-01 14:00:00'),
|
||||
('ORD20240901005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 5, '2024-09-01 15:15:00'),
|
||||
('ORD20240910001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 6, '2024-09-10 10:45:00'),
|
||||
('ORD20240910002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-09-10 12:00:00'),
|
||||
('ORD20240910003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-09-10 14:15:00'),
|
||||
('ORD20240920001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-09-20 16:30:00'),
|
||||
('ORD20240920002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-09-20 17:45:00'),
|
||||
('ORD20240920003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-09-20 19:00:00'),
|
||||
('ORD20240925001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-09-25 09:15:00'),
|
||||
('ORD20240925002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 3, '2024-09-25 10:30:00'),
|
||||
('ORD20240925003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 4, '2024-09-25 14:45:00'),
|
||||
('ORD20240930001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-09-30 15:00:00'),
|
||||
|
||||
-- 2024年10月订单
|
||||
('ORD20241001001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-10-01 09:45:00'),
|
||||
('ORD20241001002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-10-01 11:00:00'),
|
||||
('ORD20241001003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-10-01 12:15:00'),
|
||||
('ORD20241001004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 9, '2024-10-01 14:30:00'),
|
||||
('ORD20241001005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 10, '2024-10-01 15:45:00'),
|
||||
('ORD20241010001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 1, '2024-10-10 10:20:00'),
|
||||
('ORD20241010002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-10-10 11:35:00'),
|
||||
('ORD20241010003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-10-10 14:50:00'),
|
||||
('ORD20241020001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-10-20 16:05:00'),
|
||||
('ORD20241020002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-10-20 17:20:00'),
|
||||
('ORD20241020003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-10-20 18:35:00'),
|
||||
('ORD20241025001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 7, '2024-10-25 09:50:00'),
|
||||
('ORD20241025002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 8, '2024-10-25 11:05:00'),
|
||||
('ORD20241025003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 9, '2024-10-25 14:20:00'),
|
||||
('ORD20241030001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-10-30 15:35:00'),
|
||||
|
||||
-- 2024年11月订单
|
||||
('ORD20241101001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-11-01 09:30:00'),
|
||||
('ORD20241101002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 2, '2024-11-01 10:45:00'),
|
||||
('ORD20241101003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-11-01 12:00:00'),
|
||||
('ORD20241101004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 4, '2024-11-01 14:15:00'),
|
||||
('ORD20241101005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 5, '2024-11-01 15:30:00'),
|
||||
('ORD20241111001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 6, '2024-11-11 10:00:00'),
|
||||
('ORD20241111002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-11-11 11:15:00'),
|
||||
('ORD20241111003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-11-11 14:30:00'),
|
||||
('ORD20241120001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-11-20 16:45:00'),
|
||||
('ORD20241120002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-11-20 18:00:00'),
|
||||
('ORD20241120003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-11-20 19:15:00'),
|
||||
('ORD20241125001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-11-25 09:30:00'),
|
||||
('ORD20241125002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 3, '2024-11-25 10:45:00'),
|
||||
('ORD20241125003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 4, '2024-11-25 14:00:00'),
|
||||
('ORD20241130001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-11-30 15:15:00'),
|
||||
|
||||
-- 2024年12月订单
|
||||
('ORD20241201001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-12-01 09:15:00'),
|
||||
('ORD20241201002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-12-01 10:30:00'),
|
||||
('ORD20241201003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-12-01 11:45:00'),
|
||||
('ORD20241201004', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 9, '2024-12-01 14:00:00'),
|
||||
('ORD20241201005', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 10, '2024-12-01 15:15:00'),
|
||||
('ORD20241210001', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 1, '2024-12-10 10:45:00'),
|
||||
('ORD20241210002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-12-10 12:00:00'),
|
||||
('ORD20241210003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-12-10 14:15:00'),
|
||||
('ORD20241220001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-12-20 16:30:00'),
|
||||
('ORD20241220002', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-12-20 17:45:00'),
|
||||
('ORD20241220003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-12-20 19:00:00'),
|
||||
('ORD20241225001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 7, '2024-12-25 09:15:00'),
|
||||
('ORD20241225002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 8, '2024-12-25 10:30:00'),
|
||||
('ORD20241225003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 9, '2024-12-25 14:45:00'),
|
||||
('ORD20241230001', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 10, '2024-12-30 15:00:00');
|
||||
@@ -1,265 +0,0 @@
|
||||
-- 插入12个月的订单数据(使用正确的用户ID)
|
||||
INSERT INTO orders (order_number, total_amount, currency, status, order_type, description, user_id, created_at) VALUES
|
||||
-- 2024年1月
|
||||
('ORD20240101001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-01-01 10:00:00'),
|
||||
('ORD20240101002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-01-01 11:00:00'),
|
||||
('ORD20240101003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-01-01 12:00:00'),
|
||||
('ORD20240101004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-01-01 13:00:00'),
|
||||
('ORD20240101005', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-01-01 14:00:00'),
|
||||
('ORD20240101006', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-01-01 15:00:00'),
|
||||
('ORD20240101007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-01-01 16:00:00'),
|
||||
('ORD20240101008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-01-01 17:00:00'),
|
||||
('ORD20240101009', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-01-01 18:00:00'),
|
||||
('ORD20240101010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-01-01 19:00:00'),
|
||||
('ORD20240102001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-01-02 09:00:00'),
|
||||
('ORD20240102002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 231, '2024-01-02 10:00:00'),
|
||||
('ORD20240102003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 3, '2024-01-02 11:00:00'),
|
||||
('ORD20240102004', 49.00, 'CNY', 'CANCELLED', 'PRODUCT', '基础视频生成', 209, '2024-01-02 12:00:00'),
|
||||
('ORD20240102005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-01-02 13:00:00'),
|
||||
('ORD20240102006', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 212, '2024-01-02 14:00:00'),
|
||||
('ORD20240102007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-01-02 15:00:00'),
|
||||
('ORD20240102008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-01-02 16:00:00'),
|
||||
('ORD20240102009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-01-02 17:00:00'),
|
||||
('ORD20240102010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-01-02 18:00:00'),
|
||||
|
||||
-- 2024年2月
|
||||
('ORD20240201001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-02-01 10:00:00'),
|
||||
('ORD20240201002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-02-01 11:00:00'),
|
||||
('ORD20240201003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-02-01 11:30:00'),
|
||||
('ORD20240201004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-02-01 12:00:00'),
|
||||
('ORD20240201005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-02-01 13:00:00'),
|
||||
('ORD20240201006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-02-01 14:00:00'),
|
||||
('ORD20240201007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-02-01 15:00:00'),
|
||||
('ORD20240201008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-02-01 16:00:00'),
|
||||
('ORD20240201009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-02-01 17:00:00'),
|
||||
('ORD20240201010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-02-01 18:00:00'),
|
||||
('ORD20240202001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-02-02 09:00:00'),
|
||||
('ORD20240202002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-02-02 10:00:00'),
|
||||
('ORD20240202003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-02-02 11:00:00'),
|
||||
('ORD20240202004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-02-02 12:00:00'),
|
||||
('ORD20240202005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-02-02 13:00:00'),
|
||||
('ORD20240202006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-02-02 14:00:00'),
|
||||
('ORD20240202007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-02-02 15:00:00'),
|
||||
('ORD20240202008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-02-02 16:00:00'),
|
||||
('ORD20240202009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-02-02 17:00:00'),
|
||||
('ORD20240202010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-02-02 18:00:00'),
|
||||
|
||||
-- 2024年3月
|
||||
('ORD20240301001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-03-01 10:00:00'),
|
||||
('ORD20240301002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-03-01 11:00:00'),
|
||||
('ORD20240301003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-03-01 11:20:00'),
|
||||
('ORD20240301004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-03-01 12:00:00'),
|
||||
('ORD20240301005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-03-01 13:00:00'),
|
||||
('ORD20240301006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-03-01 14:00:00'),
|
||||
('ORD20240301007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-03-01 15:00:00'),
|
||||
('ORD20240301008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-03-01 16:00:00'),
|
||||
('ORD20240301009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-03-01 17:00:00'),
|
||||
('ORD20240301010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-03-01 18:00:00'),
|
||||
('ORD20240322001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-03-22 14:30:00'),
|
||||
('ORD20240322002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-03-22 14:40:00'),
|
||||
('ORD20240322003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-03-22 14:45:00'),
|
||||
('ORD20240322004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-03-22 14:50:00'),
|
||||
('ORD20240322005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-03-22 14:55:00'),
|
||||
('ORD20240322006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-03-22 15:00:00'),
|
||||
('ORD20240322007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-03-22 15:05:00'),
|
||||
('ORD20240322008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-03-22 15:10:00'),
|
||||
('ORD20240322009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-03-22 15:15:00'),
|
||||
('ORD20240322010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-03-22 15:20:00'),
|
||||
|
||||
-- 2024年4月
|
||||
('ORD20240401001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-04-01 10:00:00'),
|
||||
('ORD20240401002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-04-01 11:00:00'),
|
||||
('ORD20240401003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-04-01 12:00:00'),
|
||||
('ORD20240401004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-04-01 13:00:00'),
|
||||
('ORD20240401005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-04-01 14:00:00'),
|
||||
('ORD20240401006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-04-01 15:00:00'),
|
||||
('ORD20240401007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-04-01 16:00:00'),
|
||||
('ORD20240401008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-04-01 17:00:00'),
|
||||
('ORD20240401009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-04-01 18:00:00'),
|
||||
('ORD20240401010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-04-01 19:00:00'),
|
||||
('ORD20240410001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-04-10 14:15:00'),
|
||||
('ORD20240410002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-04-10 14:18:00'),
|
||||
('ORD20240410003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-04-10 14:20:00'),
|
||||
('ORD20240410004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-04-10 14:25:00'),
|
||||
('ORD20240410005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-04-10 14:30:00'),
|
||||
('ORD20240410006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-04-10 14:35:00'),
|
||||
('ORD20240410007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-04-10 14:40:00'),
|
||||
('ORD20240410008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-04-10 14:45:00'),
|
||||
('ORD20240410009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-04-10 14:50:00'),
|
||||
('ORD20240410010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-04-10 14:55:00'),
|
||||
|
||||
-- 2024年5月
|
||||
('ORD20240501001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-05-01 10:00:00'),
|
||||
('ORD20240501002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-05-01 11:00:00'),
|
||||
('ORD20240501003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-05-01 11:20:00'),
|
||||
('ORD20240501004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-05-01 12:00:00'),
|
||||
('ORD20240501005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-05-01 13:00:00'),
|
||||
('ORD20240501006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-05-01 14:00:00'),
|
||||
('ORD20240501007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-05-01 15:00:00'),
|
||||
('ORD20240501008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-05-01 16:00:00'),
|
||||
('ORD20240501009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-05-01 17:00:00'),
|
||||
('ORD20240501010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-05-01 18:00:00'),
|
||||
('ORD20240525001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-05-25 11:00:00'),
|
||||
('ORD20240525002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-05-25 11:05:00'),
|
||||
('ORD20240525003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-05-25 11:10:00'),
|
||||
('ORD20240525004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-05-25 11:15:00'),
|
||||
('ORD20240525005', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-05-25 11:20:00'),
|
||||
('ORD20240525006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-05-25 11:25:00'),
|
||||
('ORD20240525007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-05-25 11:30:00'),
|
||||
('ORD20240525008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-05-25 11:35:00'),
|
||||
('ORD20240525009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-05-25 11:40:00'),
|
||||
('ORD20240525010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-05-25 11:45:00'),
|
||||
|
||||
-- 2024年6月
|
||||
('ORD20240601001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-06-01 10:00:00'),
|
||||
('ORD20240601002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-06-01 11:00:00'),
|
||||
('ORD20240601003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-06-01 12:00:00'),
|
||||
('ORD20240601004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-06-01 13:00:00'),
|
||||
('ORD20240601005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-06-01 14:00:00'),
|
||||
('ORD20240601006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-06-01 15:00:00'),
|
||||
('ORD20240601007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-06-01 16:00:00'),
|
||||
('ORD20240601008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-06-01 17:00:00'),
|
||||
('ORD20240601009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-06-01 18:00:00'),
|
||||
('ORD20240601010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-06-01 19:00:00'),
|
||||
('ORD20240615001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-06-15 14:10:00'),
|
||||
('ORD20240615002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-06-15 14:12:00'),
|
||||
('ORD20240615003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-06-15 14:15:00'),
|
||||
('ORD20240615004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-06-15 14:18:00'),
|
||||
('ORD20240615005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-06-15 14:20:00'),
|
||||
('ORD20240615006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-06-15 14:25:00'),
|
||||
('ORD20240615007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-06-15 14:30:00'),
|
||||
('ORD20240615008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-06-15 14:35:00'),
|
||||
('ORD20240615009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-06-15 14:40:00'),
|
||||
('ORD20240615010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-06-15 14:45:00'),
|
||||
|
||||
-- 2024年7月
|
||||
('ORD20240701001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-07-01 10:00:00'),
|
||||
('ORD20240701002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-07-01 11:00:00'),
|
||||
('ORD20240701003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-07-01 12:15:00'),
|
||||
('ORD20240701004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-07-01 13:00:00'),
|
||||
('ORD20240701005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-07-01 14:00:00'),
|
||||
('ORD20240701006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-07-01 15:00:00'),
|
||||
('ORD20240701007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-07-01 16:00:00'),
|
||||
('ORD20240701008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-07-01 17:00:00'),
|
||||
('ORD20240701009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-07-01 18:00:00'),
|
||||
('ORD20240701010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-07-01 19:00:00'),
|
||||
('ORD20240720001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-07-20 16:00:00'),
|
||||
('ORD20240720002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-07-20 16:05:00'),
|
||||
('ORD20240720003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-07-20 16:10:00'),
|
||||
('ORD20240720004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-07-20 16:15:00'),
|
||||
('ORD20240720005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-07-20 16:20:00'),
|
||||
('ORD20240720006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-07-20 16:25:00'),
|
||||
('ORD20240720007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-07-20 16:30:00'),
|
||||
('ORD20240720008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-07-20 16:35:00'),
|
||||
('ORD20240720009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-07-20 16:40:00'),
|
||||
('ORD20240720010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-07-20 16:45:00'),
|
||||
|
||||
-- 2024年8月
|
||||
('ORD20240801001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-08-01 10:00:00'),
|
||||
('ORD20240801002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-08-01 11:00:00'),
|
||||
('ORD20240801003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-08-01 12:00:00'),
|
||||
('ORD20240801004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-08-01 13:00:00'),
|
||||
('ORD20240801005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-08-01 14:00:00'),
|
||||
('ORD20240801006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-08-01 15:00:00'),
|
||||
('ORD20240801007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-08-01 16:00:00'),
|
||||
('ORD20240801008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-08-01 17:00:00'),
|
||||
('ORD20240801009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-08-01 18:00:00'),
|
||||
('ORD20240801010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-08-01 19:00:00'),
|
||||
('ORD20240815001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-08-15 15:00:00'),
|
||||
('ORD20240815002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-08-15 15:05:00'),
|
||||
('ORD20240815003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-08-15 15:10:00'),
|
||||
('ORD20240815004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-08-15 15:15:00'),
|
||||
('ORD20240815005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-08-15 15:20:00'),
|
||||
('ORD20240815006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-08-15 15:25:00'),
|
||||
('ORD20240815007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-08-15 15:30:00'),
|
||||
('ORD20240815008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-08-15 15:35:00'),
|
||||
('ORD20240815009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-08-15 15:40:00'),
|
||||
('ORD20240815010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-08-15 15:45:00'),
|
||||
|
||||
-- 2024年9月
|
||||
('ORD20240901001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-09-01 10:00:00'),
|
||||
('ORD20240901002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-09-01 11:00:00'),
|
||||
('ORD20240901003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-09-01 12:00:00'),
|
||||
('ORD20240901004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-09-01 13:00:00'),
|
||||
('ORD20240901005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-09-01 14:00:00'),
|
||||
('ORD20240901006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-09-01 15:00:00'),
|
||||
('ORD20240901007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-09-01 16:00:00'),
|
||||
('ORD20240901008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-09-01 17:00:00'),
|
||||
('ORD20240901009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-09-01 18:00:00'),
|
||||
('ORD20240901010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-09-01 19:00:00'),
|
||||
('ORD20240910001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-09-10 17:00:00'),
|
||||
('ORD20240910002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-09-10 17:05:00'),
|
||||
('ORD20240910003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-09-10 17:10:00'),
|
||||
('ORD20240910004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-09-10 17:15:00'),
|
||||
('ORD20240910005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-09-10 17:20:00'),
|
||||
('ORD20240910006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-09-10 17:25:00'),
|
||||
('ORD20240910007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-09-10 17:30:00'),
|
||||
('ORD20240910008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-09-10 17:35:00'),
|
||||
('ORD20240910009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-09-10 17:40:00'),
|
||||
('ORD20240910010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-09-10 17:45:00'),
|
||||
|
||||
-- 2024年10月
|
||||
('ORD20241001001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-10-01 10:00:00'),
|
||||
('ORD20241001002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-10-01 11:00:00'),
|
||||
('ORD20241001003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-10-01 12:00:00'),
|
||||
('ORD20241001004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-10-01 13:00:00'),
|
||||
('ORD20241001005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-10-01 14:00:00'),
|
||||
('ORD20241001006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-10-01 15:00:00'),
|
||||
('ORD20241001007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-10-01 16:00:00'),
|
||||
('ORD20241001008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-10-01 17:00:00'),
|
||||
('ORD20241001009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-10-01 18:00:00'),
|
||||
('ORD20241001010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-10-01 19:00:00'),
|
||||
('ORD20241020001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-10-20 18:00:00'),
|
||||
('ORD20241020002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-10-20 18:05:00'),
|
||||
('ORD20241020003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-10-20 18:10:00'),
|
||||
('ORD20241020004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-10-20 18:15:00'),
|
||||
('ORD20241020005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 211, '2024-10-20 18:20:00'),
|
||||
('ORD20241020006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 212, '2024-10-20 18:25:00'),
|
||||
('ORD20241020007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 213, '2024-10-20 18:30:00'),
|
||||
('ORD20241020008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 231, '2024-10-20 18:35:00'),
|
||||
('ORD20241020009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-10-20 18:40:00'),
|
||||
('ORD20241020010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 209, '2024-10-20 18:45:00'),
|
||||
|
||||
-- 2024年11月
|
||||
('ORD20241101001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-11-01 10:00:00'),
|
||||
('ORD20241101002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-11-01 11:00:00'),
|
||||
('ORD20241101003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-11-01 12:00:00'),
|
||||
('ORD20241101004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-11-01 13:00:00'),
|
||||
('ORD20241101005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-11-01 14:00:00'),
|
||||
('ORD20241101006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-11-01 15:00:00'),
|
||||
('ORD20241101007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-11-01 16:00:00'),
|
||||
('ORD20241101008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-11-01 17:00:00'),
|
||||
('ORD20241101009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-11-01 18:00:00'),
|
||||
('ORD20241101010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-11-01 19:00:00'),
|
||||
('ORD20241115001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-11-15 19:00:00'),
|
||||
('ORD20241115002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-11-15 19:05:00'),
|
||||
('ORD20241115003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-11-15 19:10:00'),
|
||||
('ORD20241115004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-11-15 19:15:00'),
|
||||
('ORD20241115005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 213, '2024-11-15 19:20:00'),
|
||||
('ORD20241115006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 231, '2024-11-15 19:25:00'),
|
||||
('ORD20241115007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-11-15 19:30:00'),
|
||||
('ORD20241115008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 209, '2024-11-15 19:35:00'),
|
||||
('ORD20241115009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 211, '2024-11-15 19:40:00'),
|
||||
('ORD20241115010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 212, '2024-11-15 19:45:00'),
|
||||
|
||||
-- 2024年12月
|
||||
('ORD20241201001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-12-01 10:00:00'),
|
||||
('ORD20241201002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-12-01 11:00:00'),
|
||||
('ORD20241201003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-12-01 12:00:00'),
|
||||
('ORD20241201004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-12-01 13:00:00'),
|
||||
('ORD20241201005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-12-01 14:00:00'),
|
||||
('ORD20241201006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-12-01 15:00:00'),
|
||||
('ORD20241201007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-12-01 16:00:00'),
|
||||
('ORD20241201008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-12-01 17:00:00'),
|
||||
('ORD20241201009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-12-01 18:00:00'),
|
||||
('ORD20241201010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-12-01 19:00:00'),
|
||||
('ORD20241220001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-12-20 20:00:00'),
|
||||
('ORD20241220002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-12-20 20:05:00'),
|
||||
('ORD20241220003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-12-20 20:10:00'),
|
||||
('ORD20241220004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-12-20 20:15:00'),
|
||||
('ORD20241220005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-12-20 20:20:00'),
|
||||
('ORD20241220006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 209, '2024-12-20 20:25:00'),
|
||||
('ORD20241220007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 211, '2024-12-20 20:30:00'),
|
||||
('ORD20241220008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 212, '2024-12-20 20:35:00'),
|
||||
('ORD20241220009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 213, '2024-12-20 20:40:00'),
|
||||
('ORD20241220010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 231, '2024-12-20 20:45:00');
|
||||
@@ -1,270 +0,0 @@
|
||||
-- 删除现有订单数据(如果需要重新插入)
|
||||
-- DELETE FROM payments WHERE order_id IN (SELECT id FROM orders);
|
||||
-- DELETE FROM order_items WHERE order_id IN (SELECT id FROM orders);
|
||||
-- DELETE FROM orders;
|
||||
|
||||
-- 插入12个月的订单数据
|
||||
INSERT INTO orders (order_number, total_amount, currency, status, order_type, description, user_id, created_at) VALUES
|
||||
-- 2024年1月
|
||||
('ORD20240101001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-01-01 10:00:00'),
|
||||
('ORD20240101002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-01-01 11:00:00'),
|
||||
('ORD20240101003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-01-01 12:00:00'),
|
||||
('ORD20240101004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-01-01 13:00:00'),
|
||||
('ORD20240101005', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 5, '2024-01-01 14:00:00'),
|
||||
('ORD20240101006', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-01-01 15:00:00'),
|
||||
('ORD20240101007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-01-01 16:00:00'),
|
||||
('ORD20240101008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 8, '2024-01-01 17:00:00'),
|
||||
('ORD20240101009', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 9, '2024-01-01 18:00:00'),
|
||||
('ORD20240101010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-01-01 19:00:00'),
|
||||
('ORD20240102001', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 1, '2024-01-02 09:00:00'),
|
||||
('ORD20240102002', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 2, '2024-01-02 10:00:00'),
|
||||
('ORD20240102003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 3, '2024-01-02 11:00:00'),
|
||||
('ORD20240102004', 49.00, 'CNY', 'CANCELLED', 'PRODUCT', '基础视频生成', 4, '2024-01-02 12:00:00'),
|
||||
('ORD20240102005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-01-02 13:00:00'),
|
||||
('ORD20240102006', 99.00, 'CNY', 'COMPLETED', 'PRODUCT', '高级视频编辑', 6, '2024-01-02 14:00:00'),
|
||||
('ORD20240102007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-01-02 15:00:00'),
|
||||
('ORD20240102008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 8, '2024-01-02 16:00:00'),
|
||||
('ORD20240102009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-01-02 17:00:00'),
|
||||
('ORD20240102010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-01-02 18:00:00'),
|
||||
|
||||
-- 2024年2月
|
||||
('ORD20240201001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-02-01 10:00:00'),
|
||||
('ORD20240201002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-02-01 11:00:00'),
|
||||
('ORD20240201003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-02-01 11:30:00'),
|
||||
('ORD20240201004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-02-01 12:00:00'),
|
||||
('ORD20240201005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-02-01 13:00:00'),
|
||||
('ORD20240201006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-02-01 14:00:00'),
|
||||
('ORD20240201007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-02-01 15:00:00'),
|
||||
('ORD20240201008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-02-01 16:00:00'),
|
||||
('ORD20240201009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-02-01 17:00:00'),
|
||||
('ORD20240201010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-02-01 18:00:00'),
|
||||
('ORD20240202001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-02-02 09:00:00'),
|
||||
('ORD20240202002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-02-02 10:00:00'),
|
||||
('ORD20240202003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-02-02 11:00:00'),
|
||||
('ORD20240202004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-02-02 12:00:00'),
|
||||
('ORD20240202005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-02-02 13:00:00'),
|
||||
('ORD20240202006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-02-02 14:00:00'),
|
||||
('ORD20240202007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-02-02 15:00:00'),
|
||||
('ORD20240202008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-02-02 16:00:00'),
|
||||
('ORD20240202009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-02-02 17:00:00'),
|
||||
('ORD20240202010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-02-02 18:00:00'),
|
||||
|
||||
-- 2024年3月
|
||||
('ORD20240301001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-03-01 10:00:00'),
|
||||
('ORD20240301002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-03-01 11:00:00'),
|
||||
('ORD20240301003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-03-01 11:20:00'),
|
||||
('ORD20240301004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-03-01 12:00:00'),
|
||||
('ORD20240301005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-03-01 13:00:00'),
|
||||
('ORD20240301006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-03-01 14:00:00'),
|
||||
('ORD20240301007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-03-01 15:00:00'),
|
||||
('ORD20240301008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-03-01 16:00:00'),
|
||||
('ORD20240301009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-03-01 17:00:00'),
|
||||
('ORD20240301010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-03-01 18:00:00'),
|
||||
('ORD20240322001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-03-22 14:30:00'),
|
||||
('ORD20240322002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-03-22 14:40:00'),
|
||||
('ORD20240322003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-03-22 14:45:00'),
|
||||
('ORD20240322004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-03-22 14:50:00'),
|
||||
('ORD20240322005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-03-22 14:55:00'),
|
||||
('ORD20240322006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-03-22 15:00:00'),
|
||||
('ORD20240322007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-03-22 15:05:00'),
|
||||
('ORD20240322008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-03-22 15:10:00'),
|
||||
('ORD20240322009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-03-22 15:15:00'),
|
||||
('ORD20240322010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-03-22 15:20:00'),
|
||||
|
||||
-- 2024年4月
|
||||
('ORD20240401001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-04-01 10:00:00'),
|
||||
('ORD20240401002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-04-01 11:00:00'),
|
||||
('ORD20240401003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-04-01 12:00:00'),
|
||||
('ORD20240401004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-04-01 13:00:00'),
|
||||
('ORD20240401005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-04-01 14:00:00'),
|
||||
('ORD20240401006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-04-01 15:00:00'),
|
||||
('ORD20240401007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-04-01 16:00:00'),
|
||||
('ORD20240401008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-04-01 17:00:00'),
|
||||
('ORD20240401009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-04-01 18:00:00'),
|
||||
('ORD20240401010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-04-01 19:00:00'),
|
||||
('ORD20240410001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-04-10 14:15:00'),
|
||||
('ORD20240410002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-04-10 14:18:00'),
|
||||
('ORD20240410003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-04-10 14:20:00'),
|
||||
('ORD20240410004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-04-10 14:25:00'),
|
||||
('ORD20240410005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-04-10 14:30:00'),
|
||||
('ORD20240410006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-04-10 14:35:00'),
|
||||
('ORD20240410007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-04-10 14:40:00'),
|
||||
('ORD20240410008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-04-10 14:45:00'),
|
||||
('ORD20240410009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-04-10 14:50:00'),
|
||||
('ORD20240410010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-04-10 14:55:00'),
|
||||
|
||||
-- 2024年5月
|
||||
('ORD20240501001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-05-01 10:00:00'),
|
||||
('ORD20240501002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-05-01 11:00:00'),
|
||||
('ORD20240501003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-05-01 11:20:00'),
|
||||
('ORD20240501004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-05-01 12:00:00'),
|
||||
('ORD20240501005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-05-01 13:00:00'),
|
||||
('ORD20240501006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-05-01 14:00:00'),
|
||||
('ORD20240501007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-05-01 15:00:00'),
|
||||
('ORD20240501008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-05-01 16:00:00'),
|
||||
('ORD20240501009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-05-01 17:00:00'),
|
||||
('ORD20240501010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-05-01 18:00:00'),
|
||||
('ORD20240525001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-05-25 11:00:00'),
|
||||
('ORD20240525002', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 2, '2024-05-25 11:05:00'),
|
||||
('ORD20240525003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-05-25 11:10:00'),
|
||||
('ORD20240525004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-05-25 11:15:00'),
|
||||
('ORD20240525005', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 5, '2024-05-25 11:20:00'),
|
||||
('ORD20240525006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-05-25 11:25:00'),
|
||||
('ORD20240525007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-05-25 11:30:00'),
|
||||
('ORD20240525008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 8, '2024-05-25 11:35:00'),
|
||||
('ORD20240525009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-05-25 11:40:00'),
|
||||
('ORD20240525010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-05-25 11:45:00'),
|
||||
|
||||
-- 2024年6月
|
||||
('ORD20240601001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-06-01 10:00:00'),
|
||||
('ORD20240601002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-06-01 11:00:00'),
|
||||
('ORD20240601003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-06-01 12:00:00'),
|
||||
('ORD20240601004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-06-01 13:00:00'),
|
||||
('ORD20240601005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-06-01 14:00:00'),
|
||||
('ORD20240601006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-06-01 15:00:00'),
|
||||
('ORD20240601007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-06-01 16:00:00'),
|
||||
('ORD20240601008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-06-01 17:00:00'),
|
||||
('ORD20240601009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-06-01 18:00:00'),
|
||||
('ORD20240601010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-06-01 19:00:00'),
|
||||
('ORD20240615001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-06-15 14:10:00'),
|
||||
('ORD20240615002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-06-15 14:12:00'),
|
||||
('ORD20240615003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-06-15 14:15:00'),
|
||||
('ORD20240615004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-06-15 14:18:00'),
|
||||
('ORD20240615005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-06-15 14:20:00'),
|
||||
('ORD20240615006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-06-15 14:25:00'),
|
||||
('ORD20240615007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-06-15 14:30:00'),
|
||||
('ORD20240615008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-06-15 14:35:00'),
|
||||
('ORD20240615009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-06-15 14:40:00'),
|
||||
('ORD20240615010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-06-15 14:45:00'),
|
||||
|
||||
-- 2024年7月
|
||||
('ORD20240701001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-07-01 10:00:00'),
|
||||
('ORD20240701002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-07-01 11:00:00'),
|
||||
('ORD20240701003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-07-01 12:15:00'),
|
||||
('ORD20240701004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-07-01 13:00:00'),
|
||||
('ORD20240701005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-07-01 14:00:00'),
|
||||
('ORD20240701006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-07-01 15:00:00'),
|
||||
('ORD20240701007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-07-01 16:00:00'),
|
||||
('ORD20240701008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-07-01 17:00:00'),
|
||||
('ORD20240701009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-07-01 18:00:00'),
|
||||
('ORD20240701010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-07-01 19:00:00'),
|
||||
('ORD20240720001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-07-20 16:00:00'),
|
||||
('ORD20240720002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-07-20 16:05:00'),
|
||||
('ORD20240720003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-07-20 16:10:00'),
|
||||
('ORD20240720004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-07-20 16:15:00'),
|
||||
('ORD20240720005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-07-20 16:20:00'),
|
||||
('ORD20240720006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-07-20 16:25:00'),
|
||||
('ORD20240720007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-07-20 16:30:00'),
|
||||
('ORD20240720008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-07-20 16:35:00'),
|
||||
('ORD20240720009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-07-20 16:40:00'),
|
||||
('ORD20240720010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-07-20 16:45:00'),
|
||||
|
||||
-- 2024年8月
|
||||
('ORD20240801001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-08-01 10:00:00'),
|
||||
('ORD20240801002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-08-01 11:00:00'),
|
||||
('ORD20240801003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-08-01 12:00:00'),
|
||||
('ORD20240801004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-08-01 13:00:00'),
|
||||
('ORD20240801005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-08-01 14:00:00'),
|
||||
('ORD20240801006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-08-01 15:00:00'),
|
||||
('ORD20240801007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-08-01 16:00:00'),
|
||||
('ORD20240801008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-08-01 17:00:00'),
|
||||
('ORD20240801009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-08-01 18:00:00'),
|
||||
('ORD20240801010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-08-01 19:00:00'),
|
||||
('ORD20240815001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-08-15 15:00:00'),
|
||||
('ORD20240815002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-08-15 15:05:00'),
|
||||
('ORD20240815003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-08-15 15:10:00'),
|
||||
('ORD20240815004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-08-15 15:15:00'),
|
||||
('ORD20240815005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-08-15 15:20:00'),
|
||||
('ORD20240815006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-08-15 15:25:00'),
|
||||
('ORD20240815007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-08-15 15:30:00'),
|
||||
('ORD20240815008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-08-15 15:35:00'),
|
||||
('ORD20240815009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-08-15 15:40:00'),
|
||||
('ORD20240815010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-08-15 15:45:00'),
|
||||
|
||||
-- 2024年9月
|
||||
('ORD20240901001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-09-01 10:00:00'),
|
||||
('ORD20240901002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-09-01 11:00:00'),
|
||||
('ORD20240901003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-09-01 12:00:00'),
|
||||
('ORD20240901004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-09-01 13:00:00'),
|
||||
('ORD20240901005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-09-01 14:00:00'),
|
||||
('ORD20240901006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-09-01 15:00:00'),
|
||||
('ORD20240901007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-09-01 16:00:00'),
|
||||
('ORD20240901008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-09-01 17:00:00'),
|
||||
('ORD20240901009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-09-01 18:00:00'),
|
||||
('ORD20240901010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-09-01 19:00:00'),
|
||||
('ORD20240910001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-09-10 17:00:00'),
|
||||
('ORD20240910002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-09-10 17:05:00'),
|
||||
('ORD20240910003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-09-10 17:10:00'),
|
||||
('ORD20240910004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-09-10 17:15:00'),
|
||||
('ORD20240910005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-09-10 17:20:00'),
|
||||
('ORD20240910006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-09-10 17:25:00'),
|
||||
('ORD20240910007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-09-10 17:30:00'),
|
||||
('ORD20240910008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-09-10 17:35:00'),
|
||||
('ORD20240910009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-09-10 17:40:00'),
|
||||
('ORD20240910010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-09-10 17:45:00'),
|
||||
|
||||
-- 2024年10月
|
||||
('ORD20241001001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-10-01 10:00:00'),
|
||||
('ORD20241001002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-10-01 11:00:00'),
|
||||
('ORD20241001003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-10-01 12:00:00'),
|
||||
('ORD20241001004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-10-01 13:00:00'),
|
||||
('ORD20241001005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-10-01 14:00:00'),
|
||||
('ORD20241001006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-10-01 15:00:00'),
|
||||
('ORD20241001007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-10-01 16:00:00'),
|
||||
('ORD20241001008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-10-01 17:00:00'),
|
||||
('ORD20241001009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-10-01 18:00:00'),
|
||||
('ORD20241001010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-10-01 19:00:00'),
|
||||
('ORD20241020001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-10-20 18:00:00'),
|
||||
('ORD20241020002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-10-20 18:05:00'),
|
||||
('ORD20241020003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-10-20 18:10:00'),
|
||||
('ORD20241020004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-10-20 18:15:00'),
|
||||
('ORD20241020005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-10-20 18:20:00'),
|
||||
('ORD20241020006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-10-20 18:25:00'),
|
||||
('ORD20241020007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-10-20 18:30:00'),
|
||||
('ORD20241020008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-10-20 18:35:00'),
|
||||
('ORD20241020009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-10-20 18:40:00'),
|
||||
('ORD20241020010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-10-20 18:45:00'),
|
||||
|
||||
-- 2024年11月
|
||||
('ORD20241101001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-11-01 10:00:00'),
|
||||
('ORD20241101002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-11-01 11:00:00'),
|
||||
('ORD20241101003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-11-01 12:00:00'),
|
||||
('ORD20241101004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-11-01 13:00:00'),
|
||||
('ORD20241101005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-11-01 14:00:00'),
|
||||
('ORD20241101006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-11-01 15:00:00'),
|
||||
('ORD20241101007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-11-01 16:00:00'),
|
||||
('ORD20241101008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-11-01 17:00:00'),
|
||||
('ORD20241101009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-11-01 18:00:00'),
|
||||
('ORD20241101010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-11-01 19:00:00'),
|
||||
('ORD20241115001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-11-15 19:00:00'),
|
||||
('ORD20241115002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-11-15 19:05:00'),
|
||||
('ORD20241115003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-11-15 19:10:00'),
|
||||
('ORD20241115004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-11-15 19:15:00'),
|
||||
('ORD20241115005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-11-15 19:20:00'),
|
||||
('ORD20241115006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-11-15 19:25:00'),
|
||||
('ORD20241115007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-11-15 19:30:00'),
|
||||
('ORD20241115008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-11-15 19:35:00'),
|
||||
('ORD20241115009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-11-15 19:40:00'),
|
||||
('ORD20241115010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-11-15 19:45:00'),
|
||||
|
||||
-- 2024年12月
|
||||
('ORD20241201001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-12-01 10:00:00'),
|
||||
('ORD20241201002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-12-01 11:00:00'),
|
||||
('ORD20241201003', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 3, '2024-12-01 12:00:00'),
|
||||
('ORD20241201004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-12-01 13:00:00'),
|
||||
('ORD20241201005', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 5, '2024-12-01 14:00:00'),
|
||||
('ORD20241201006', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 6, '2024-12-01 15:00:00'),
|
||||
('ORD20241201007', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 7, '2024-12-01 16:00:00'),
|
||||
('ORD20241201008', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 8, '2024-12-01 17:00:00'),
|
||||
('ORD20241201009', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 9, '2024-12-01 18:00:00'),
|
||||
('ORD20241201010', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 10, '2024-12-01 19:00:00'),
|
||||
('ORD20241220001', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 1, '2024-12-20 20:00:00'),
|
||||
('ORD20241220002', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 2, '2024-12-20 20:05:00'),
|
||||
('ORD20241220003', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 3, '2024-12-20 20:10:00'),
|
||||
('ORD20241220004', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 4, '2024-12-20 20:15:00'),
|
||||
('ORD20241220005', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 5, '2024-12-20 20:20:00'),
|
||||
('ORD20241220006', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 6, '2024-12-20 20:25:00'),
|
||||
('ORD20241220007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-12-20 20:30:00'),
|
||||
('ORD20241220008', 199.00, 'CNY', 'COMPLETED', 'PRODUCT', '视频生成服务包', 8, '2024-12-20 20:35:00'),
|
||||
('ORD20241220009', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 9, '2024-12-20 20:40:00'),
|
||||
('ORD20241220010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-12-20 20:45:00');
|
||||
@@ -1,207 +0,0 @@
|
||||
-- 12个月支付数据
|
||||
-- 对应orders_12months.sql中的订单
|
||||
|
||||
INSERT IGNORE INTO payments (order_id, amount, currency, payment_method, status, description, user_id, created_at, paid_at) VALUES
|
||||
-- 2024年1月支付数据
|
||||
('ORD20240101001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 1, '2024-01-01 10:00:00', '2024-01-01 10:05:00'),
|
||||
('ORD20240101002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 2, '2024-01-01 11:00:00', '2024-01-01 11:02:00'),
|
||||
('ORD20240101003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 3, '2024-01-01 12:00:00', '2024-01-01 12:03:00'),
|
||||
('ORD20240101004', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 4, '2024-01-01 13:00:00', '2024-01-01 13:04:00'),
|
||||
('ORD20240101005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 5, '2024-01-01 14:00:00', '2024-01-01 14:05:00'),
|
||||
('ORD20240101006', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 6, '2024-01-01 15:00:00', '2024-01-01 15:02:00'),
|
||||
('ORD20240101007', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 7, '2024-01-01 16:00:00', '2024-01-01 16:03:00'),
|
||||
('ORD20240101008', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 8, '2024-01-01 17:00:00', '2024-01-01 17:04:00'),
|
||||
('ORD20240101009', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 9, '2024-01-01 18:00:00', '2024-01-01 18:02:00'),
|
||||
('ORD20240101010', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 10, '2024-01-01 19:00:00', '2024-01-01 19:03:00'),
|
||||
('ORD20240102001', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 1, '2024-01-02 09:00:00', '2024-01-02 09:05:00'),
|
||||
('ORD20240102002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 2, '2024-01-02 10:00:00', '2024-01-02 10:03:00'),
|
||||
('ORD20240102003', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 3, '2024-01-02 11:00:00', '2024-01-02 11:05:00'),
|
||||
('ORD20240102004', 49.00, 'CNY', 'WECHAT', 'CANCELLED', '基础视频生成', 4, '2024-01-02 12:00:00', NULL),
|
||||
('ORD20240102005', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 5, '2024-01-02 13:00:00', '2024-01-02 13:04:00'),
|
||||
('ORD20240115001', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 6, '2024-01-15 14:30:00', '2024-01-15 14:35:00'),
|
||||
('ORD20240115002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 7, '2024-01-15 15:45:00', '2024-01-15 15:48:00'),
|
||||
('ORD20240115003', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 8, '2024-01-15 16:20:00', '2024-01-15 16:22:00'),
|
||||
('ORD20240128001', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 9, '2024-01-28 10:15:00', '2024-01-28 10:19:00'),
|
||||
('ORD20240128002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 10, '2024-01-28 11:30:00', '2024-01-28 11:34:00'),
|
||||
|
||||
-- 2024年2月支付数据
|
||||
('ORD20240201001', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 1, '2024-02-01 09:00:00', '2024-02-01 09:02:00'),
|
||||
('ORD20240201002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 2, '2024-02-01 10:15:00', '2024-02-01 10:19:00'),
|
||||
('ORD20240201003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 3, '2024-02-01 11:30:00', '2024-02-01 11:35:00'),
|
||||
('ORD20240201004', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 4, '2024-02-01 14:20:00', '2024-02-01 14:24:00'),
|
||||
('ORD20240201005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 5, '2024-02-01 15:45:00', '2024-02-01 15:48:00'),
|
||||
('ORD20240214001', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 6, '2024-02-14 10:00:00', '2024-02-14 10:05:00'),
|
||||
('ORD20240214002', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 7, '2024-02-14 11:30:00', '2024-02-14 11:32:00'),
|
||||
('ORD20240214003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 8, '2024-02-14 14:15:00', '2024-02-14 14:20:00'),
|
||||
('ORD20240225001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 9, '2024-02-25 16:00:00', '2024-02-25 16:04:00'),
|
||||
('ORD20240225002', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 10, '2024-02-25 17:30:00', '2024-02-25 17:34:00'),
|
||||
|
||||
-- 2024年3月支付数据
|
||||
('ORD20240301001', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-03-01 09:30:00', '2024-03-01 09:32:00'),
|
||||
('ORD20240301002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 2, '2024-03-01 10:45:00', '2024-03-01 10:49:00'),
|
||||
('ORD20240301003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-03-01 11:20:00', '2024-03-01 11:25:00'),
|
||||
('ORD20240301004', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 4, '2024-03-01 14:10:00', '2024-03-01 14:14:00'),
|
||||
('ORD20240301005', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 5, '2024-03-01 15:25:00', '2024-03-01 15:28:00'),
|
||||
('ORD20240308001', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 6, '2024-03-08 10:00:00', '2024-03-08 10:05:00'),
|
||||
('ORD20240308002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 7, '2024-03-08 11:15:00', '2024-03-08 11:17:00'),
|
||||
('ORD20240308003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-03-08 14:30:00', '2024-03-08 14:35:00'),
|
||||
('ORD20240315001', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 9, '2024-03-15 16:45:00', '2024-03-15 16:49:00'),
|
||||
('ORD20240315002', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-03-15 17:20:00', '2024-03-15 17:24:00'),
|
||||
('ORD20240322001', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-03-22 09:15:00', '2024-03-22 09:17:00'),
|
||||
('ORD20240322002', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 2, '2024-03-22 10:30:00', '2024-03-22 10:35:00'),
|
||||
('ORD20240322003', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 3, '2024-03-22 14:45:00', '2024-03-22 14:48:00'),
|
||||
('ORD20240330001', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 4, '2024-03-30 11:00:00', '2024-03-30 11:05:00'),
|
||||
('ORD20240330002', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-03-30 15:30:00', '2024-03-30 15:34:00'),
|
||||
|
||||
-- 2024年4月支付数据
|
||||
('ORD20240401001', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-04-01 09:00:00', '2024-04-01 09:02:00'),
|
||||
('ORD20240401002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 7, '2024-04-01 10:20:00', '2024-04-01 10:24:00'),
|
||||
('ORD20240401003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-04-01 11:40:00', '2024-04-01 11:45:00'),
|
||||
('ORD20240401004', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 9, '2024-04-01 14:15:00', '2024-04-01 14:19:00'),
|
||||
('ORD20240401005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 10, '2024-04-01 15:35:00', '2024-04-01 15:38:00'),
|
||||
('ORD20240410001', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 1, '2024-04-10 10:30:00', '2024-04-10 10:35:00'),
|
||||
('ORD20240410002', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 2, '2024-04-10 11:45:00', '2024-04-10 11:47:00'),
|
||||
('ORD20240410003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-04-10 14:20:00', '2024-04-10 14:25:00'),
|
||||
('ORD20240420001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 4, '2024-04-20 16:10:00', '2024-04-20 16:14:00'),
|
||||
('ORD20240420002', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-04-20 17:25:00', '2024-04-20 17:29:00'),
|
||||
('ORD20240420003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-04-20 18:40:00', '2024-04-20 18:42:00'),
|
||||
('ORD20240425001', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 7, '2024-04-25 09:15:00', '2024-04-25 09:20:00'),
|
||||
('ORD20240425002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 8, '2024-04-25 10:30:00', '2024-04-25 10:33:00'),
|
||||
('ORD20240425003', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 9, '2024-04-25 14:45:00', '2024-04-25 14:50:00'),
|
||||
('ORD20240430001', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-04-30 15:20:00', '2024-04-30 15:24:00'),
|
||||
|
||||
-- 2024年5月支付数据
|
||||
('ORD20240501001', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-05-01 09:30:00', '2024-05-01 09:32:00'),
|
||||
('ORD20240501002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 2, '2024-05-01 10:45:00', '2024-05-01 10:49:00'),
|
||||
('ORD20240501003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-05-01 11:20:00', '2024-05-01 11:25:00'),
|
||||
('ORD20240501004', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 4, '2024-05-01 14:10:00', '2024-05-01 14:14:00'),
|
||||
('ORD20240501005', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 5, '2024-05-01 15:25:00', '2024-05-01 15:28:00'),
|
||||
('ORD20240510001', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 6, '2024-05-10 10:00:00', '2024-05-10 10:05:00'),
|
||||
('ORD20240510002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 7, '2024-05-10 11:15:00', '2024-05-10 11:17:00'),
|
||||
('ORD20240510003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-05-10 14:30:00', '2024-05-10 14:35:00'),
|
||||
('ORD20240520001', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 9, '2024-05-20 16:45:00', '2024-05-20 16:49:00'),
|
||||
('ORD20240520002', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-05-20 17:20:00', '2024-05-20 17:24:00'),
|
||||
('ORD20240520003', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-05-20 18:35:00', '2024-05-20 18:37:00'),
|
||||
('ORD20240525001', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 2, '2024-05-25 09:50:00', '2024-05-25 09:55:00'),
|
||||
('ORD20240525002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 3, '2024-05-25 11:05:00', '2024-05-25 11:08:00'),
|
||||
('ORD20240525003', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 4, '2024-05-25 14:20:00', '2024-05-25 14:25:00'),
|
||||
('ORD20240530001', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-05-30 15:35:00', '2024-05-30 15:39:00'),
|
||||
|
||||
-- 2024年6月支付数据
|
||||
('ORD20240601001', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-06-01 09:15:00', '2024-06-01 09:17:00'),
|
||||
('ORD20240601002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 7, '2024-06-01 10:30:00', '2024-06-01 10:34:00'),
|
||||
('ORD20240601003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-06-01 11:45:00', '2024-06-01 11:50:00'),
|
||||
('ORD20240601004', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 9, '2024-06-01 14:00:00', '2024-06-01 14:04:00'),
|
||||
('ORD20240601005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 10, '2024-06-01 15:15:00', '2024-06-01 15:18:00'),
|
||||
('ORD20240615001', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 1, '2024-06-15 10:45:00', '2024-06-15 10:50:00'),
|
||||
('ORD20240615002', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 2, '2024-06-15 12:00:00', '2024-06-15 12:02:00'),
|
||||
('ORD20240615003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-06-15 14:15:00', '2024-06-15 14:20:00'),
|
||||
('ORD20240620001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 4, '2024-06-20 16:30:00', '2024-06-20 16:34:00'),
|
||||
('ORD20240620002', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-06-20 17:45:00', '2024-06-20 17:49:00'),
|
||||
('ORD20240620003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-06-20 19:00:00', '2024-06-20 19:02:00'),
|
||||
('ORD20240625001', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 7, '2024-06-25 09:30:00', '2024-06-25 09:35:00'),
|
||||
('ORD20240625002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 8, '2024-06-25 10:45:00', '2024-06-25 10:48:00'),
|
||||
('ORD20240625003', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 9, '2024-06-25 14:00:00', '2024-06-25 14:05:00'),
|
||||
('ORD20240630001', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-06-30 15:15:00', '2024-06-30 15:19:00'),
|
||||
|
||||
-- 2024年7月支付数据
|
||||
('ORD20240701001', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-07-01 09:45:00', '2024-07-01 09:47:00'),
|
||||
('ORD20240701002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 2, '2024-07-01 11:00:00', '2024-07-01 11:04:00'),
|
||||
('ORD20240701003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-07-01 12:15:00', '2024-07-01 12:20:00'),
|
||||
('ORD20240701004', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 4, '2024-07-01 14:30:00', '2024-07-01 14:34:00'),
|
||||
('ORD20240701005', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 5, '2024-07-01 15:45:00', '2024-07-01 15:48:00'),
|
||||
('ORD20240710001', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 6, '2024-07-10 10:20:00', '2024-07-10 10:25:00'),
|
||||
('ORD20240710002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 7, '2024-07-10 11:35:00', '2024-07-10 11:37:00'),
|
||||
('ORD20240710003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-07-10 14:50:00', '2024-07-10 14:55:00'),
|
||||
('ORD20240720001', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 9, '2024-07-20 16:05:00', '2024-07-20 16:09:00'),
|
||||
('ORD20240720002', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-07-20 17:20:00', '2024-07-20 17:24:00'),
|
||||
('ORD20240720003', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-07-20 18:35:00', '2024-07-20 18:37:00'),
|
||||
('ORD20240725001', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 2, '2024-07-25 09:50:00', '2024-07-25 09:55:00'),
|
||||
('ORD20240725002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 3, '2024-07-25 11:05:00', '2024-07-25 11:08:00'),
|
||||
('ORD20240725003', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 4, '2024-07-25 14:20:00', '2024-07-25 14:25:00'),
|
||||
('ORD20240730001', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-07-30 15:35:00', '2024-07-30 15:39:00'),
|
||||
|
||||
-- 2024年8月支付数据
|
||||
('ORD20240801001', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-08-01 09:30:00', '2024-08-01 09:32:00'),
|
||||
('ORD20240801002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 7, '2024-08-01 10:45:00', '2024-08-01 10:49:00'),
|
||||
('ORD20240801003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-08-01 12:00:00', '2024-08-01 12:05:00'),
|
||||
('ORD20240801004', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 9, '2024-08-01 14:15:00', '2024-08-01 14:19:00'),
|
||||
('ORD20240801005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 10, '2024-08-01 15:30:00', '2024-08-01 15:33:00'),
|
||||
('ORD20240810001', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 1, '2024-08-10 10:00:00', '2024-08-10 10:05:00'),
|
||||
('ORD20240810002', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 2, '2024-08-10 11:15:00', '2024-08-10 11:17:00'),
|
||||
('ORD20240810003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-08-10 14:30:00', '2024-08-10 14:35:00'),
|
||||
('ORD20240820001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 4, '2024-08-20 16:45:00', '2024-08-20 16:49:00'),
|
||||
('ORD20240820002', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-08-20 18:00:00', '2024-08-20 18:04:00'),
|
||||
('ORD20240820003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-08-20 19:15:00', '2024-08-20 19:17:00'),
|
||||
('ORD20240825001', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 7, '2024-08-25 09:30:00', '2024-08-25 09:35:00'),
|
||||
('ORD20240825002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 8, '2024-08-25 10:45:00', '2024-08-25 10:48:00'),
|
||||
('ORD20240825003', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 9, '2024-08-25 14:00:00', '2024-08-25 14:05:00'),
|
||||
('ORD20240830001', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-08-30 15:15:00', '2024-08-30 15:19:00'),
|
||||
|
||||
-- 2024年9月支付数据
|
||||
('ORD20240901001', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-09-01 09:15:00', '2024-09-01 09:17:00'),
|
||||
('ORD20240901002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 2, '2024-09-01 10:30:00', '2024-09-01 10:34:00'),
|
||||
('ORD20240901003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-09-01 11:45:00', '2024-09-01 11:50:00'),
|
||||
('ORD20240901004', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 4, '2024-09-01 14:00:00', '2024-09-01 14:04:00'),
|
||||
('ORD20240901005', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 5, '2024-09-01 15:15:00', '2024-09-01 15:18:00'),
|
||||
('ORD20240910001', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 6, '2024-09-10 10:45:00', '2024-09-10 10:50:00'),
|
||||
('ORD20240910002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 7, '2024-09-10 12:00:00', '2024-09-10 12:02:00'),
|
||||
('ORD20240910003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-09-10 14:15:00', '2024-09-10 14:20:00'),
|
||||
('ORD20240920001', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 9, '2024-09-20 16:30:00', '2024-09-20 16:34:00'),
|
||||
('ORD20240920002', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-09-20 17:45:00', '2024-09-20 17:49:00'),
|
||||
('ORD20240920003', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-09-20 19:00:00', '2024-09-20 19:02:00'),
|
||||
('ORD20240925001', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 2, '2024-09-25 09:15:00', '2024-09-25 09:20:00'),
|
||||
('ORD20240925002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 3, '2024-09-25 10:30:00', '2024-09-25 10:33:00'),
|
||||
('ORD20240925003', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 4, '2024-09-25 14:45:00', '2024-09-25 14:50:00'),
|
||||
('ORD20240930001', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-09-30 15:00:00', '2024-09-30 15:04:00'),
|
||||
|
||||
-- 2024年10月支付数据
|
||||
('ORD20241001001', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-10-01 09:45:00', '2024-10-01 09:47:00'),
|
||||
('ORD20241001002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 7, '2024-10-01 11:00:00', '2024-10-01 11:04:00'),
|
||||
('ORD20241001003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-10-01 12:15:00', '2024-10-01 12:20:00'),
|
||||
('ORD20241001004', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 9, '2024-10-01 14:30:00', '2024-10-01 14:34:00'),
|
||||
('ORD20241001005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 10, '2024-10-01 15:45:00', '2024-10-01 15:48:00'),
|
||||
('ORD20241010001', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 1, '2024-10-10 10:20:00', '2024-10-10 10:25:00'),
|
||||
('ORD20241010002', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 2, '2024-10-10 11:35:00', '2024-10-10 11:37:00'),
|
||||
('ORD20241010003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-10-10 14:50:00', '2024-10-10 14:55:00'),
|
||||
('ORD20241020001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 4, '2024-10-20 16:05:00', '2024-10-20 16:09:00'),
|
||||
('ORD20241020002', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-10-20 17:20:00', '2024-10-20 17:24:00'),
|
||||
('ORD20241020003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-10-20 18:35:00', '2024-10-20 18:37:00'),
|
||||
('ORD20241025001', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 7, '2024-10-25 09:50:00', '2024-10-25 09:55:00'),
|
||||
('ORD20241025002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 8, '2024-10-25 11:05:00', '2024-10-25 11:08:00'),
|
||||
('ORD20241025003', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 9, '2024-10-25 14:20:00', '2024-10-25 14:25:00'),
|
||||
('ORD20241030001', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-10-30 15:35:00', '2024-10-30 15:39:00'),
|
||||
|
||||
-- 2024年11月支付数据
|
||||
('ORD20241101001', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-11-01 09:30:00', '2024-11-01 09:32:00'),
|
||||
('ORD20241101002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 2, '2024-11-01 10:45:00', '2024-11-01 10:49:00'),
|
||||
('ORD20241101003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-11-01 12:00:00', '2024-11-01 12:05:00'),
|
||||
('ORD20241101004', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 4, '2024-11-01 14:15:00', '2024-11-01 14:19:00'),
|
||||
('ORD20241101005', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 5, '2024-11-01 15:30:00', '2024-11-01 15:33:00'),
|
||||
('ORD20241111001', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 6, '2024-11-11 10:00:00', '2024-11-11 10:05:00'),
|
||||
('ORD20241111002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 7, '2024-11-11 11:15:00', '2024-11-11 11:17:00'),
|
||||
('ORD20241111003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-11-11 14:30:00', '2024-11-11 14:35:00'),
|
||||
('ORD20241120001', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 9, '2024-11-20 16:45:00', '2024-11-20 16:49:00'),
|
||||
('ORD20241120002', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-11-20 18:00:00', '2024-11-20 18:04:00'),
|
||||
('ORD20241120003', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 1, '2024-11-20 19:15:00', '2024-11-20 19:17:00'),
|
||||
('ORD20241125001', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 2, '2024-11-25 09:30:00', '2024-11-25 09:35:00'),
|
||||
('ORD20241125002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '高级视频编辑', 3, '2024-11-25 10:45:00', '2024-11-25 10:48:00'),
|
||||
('ORD20241125003', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 4, '2024-11-25 14:00:00', '2024-11-25 14:05:00'),
|
||||
('ORD20241130001', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-11-30 15:15:00', '2024-11-30 15:19:00'),
|
||||
|
||||
-- 2024年12月支付数据
|
||||
('ORD20241201001', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-12-01 09:15:00', '2024-12-01 09:17:00'),
|
||||
('ORD20241201002', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 7, '2024-12-01 10:30:00', '2024-12-01 10:34:00'),
|
||||
('ORD20241201003', 199.00, 'CNY', 'ALIPAY', 'COMPLETED', '视频生成服务包', 8, '2024-12-01 11:45:00', '2024-12-01 11:50:00'),
|
||||
('ORD20241201004', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 9, '2024-12-01 14:00:00', '2024-12-01 14:04:00'),
|
||||
('ORD20241201005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 10, '2024-12-01 15:15:00', '2024-12-01 15:18:00'),
|
||||
('ORD20241210001', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 1, '2024-12-10 10:45:00', '2024-12-10 10:50:00'),
|
||||
('ORD20241210002', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 2, '2024-12-10 12:00:00', '2024-12-10 12:02:00'),
|
||||
('ORD20241210003', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 3, '2024-12-10 14:15:00', '2024-12-10 14:20:00'),
|
||||
('ORD20241220001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 4, '2024-12-20 16:30:00', '2024-12-20 16:34:00'),
|
||||
('ORD20241220002', 149.00, 'CNY', 'WECHAT', 'COMPLETED', '专业视频制作', 5, '2024-12-20 17:45:00', '2024-12-20 17:49:00'),
|
||||
('ORD20241220003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 6, '2024-12-20 19:00:00', '2024-12-20 19:02:00'),
|
||||
('ORD20241225001', 199.00, 'CNY', 'WECHAT', 'COMPLETED', '视频生成服务包', 7, '2024-12-25 09:15:00', '2024-12-25 09:20:00'),
|
||||
('ORD20241225002', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '高级视频编辑', 8, '2024-12-25 10:30:00', '2024-12-25 10:33:00'),
|
||||
('ORD20241225003', 299.00, 'CNY', 'WECHAT', 'COMPLETED', '企业会员订阅', 9, '2024-12-25 14:45:00', '2024-12-25 14:50:00'),
|
||||
('ORD20241230001', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 10, '2024-12-30 15:00:00', '2024-12-30 15:04:00');
|
||||
@@ -1,24 +0,0 @@
|
||||
-- 插入支付数据
|
||||
INSERT INTO payments (order_id, user_id, amount, currency, payment_method, status, external_transaction_id, paid_at, created_at)
|
||||
SELECT
|
||||
o.id as order_id,
|
||||
o.user_id,
|
||||
o.total_amount as amount,
|
||||
o.currency,
|
||||
CASE
|
||||
WHEN RAND() < 0.6 THEN 'ALIPAY'
|
||||
WHEN RAND() < 0.8 THEN 'WECHAT'
|
||||
ELSE 'PAYPAL'
|
||||
END as payment_method,
|
||||
CASE
|
||||
WHEN o.status = 'COMPLETED' THEN 'COMPLETED'
|
||||
WHEN o.status = 'CANCELLED' THEN 'CANCELLED'
|
||||
ELSE 'PENDING'
|
||||
END as status,
|
||||
CONCAT('TXN_', UNIX_TIMESTAMP(o.created_at), '_', o.id) as external_transaction_id,
|
||||
CASE
|
||||
WHEN o.status = 'COMPLETED' THEN DATE_ADD(o.created_at, INTERVAL FLOOR(RAND() * 30) MINUTE)
|
||||
ELSE NULL
|
||||
END as paid_at,
|
||||
o.created_at
|
||||
FROM orders o;
|
||||
@@ -580,6 +580,10 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -496,6 +496,10 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -535,6 +535,10 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,407 +0,0 @@
|
||||
-- 用户活跃度统计表
|
||||
CREATE TABLE IF NOT EXISTS user_activity_stats (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
stat_date DATE NOT NULL COMMENT '统计日期',
|
||||
daily_active_users INT NOT NULL DEFAULT 0 COMMENT '日活用户数',
|
||||
monthly_active_users INT NOT NULL DEFAULT 0 COMMENT '月活用户数',
|
||||
new_users INT NOT NULL DEFAULT 0 COMMENT '新增用户数',
|
||||
returning_users INT NOT NULL DEFAULT 0 COMMENT '回访用户数',
|
||||
session_count INT NOT NULL DEFAULT 0 COMMENT '会话数',
|
||||
avg_session_duration DECIMAL(10,2) DEFAULT 0 COMMENT '平均会话时长(分钟)',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_stat_date (stat_date),
|
||||
INDEX idx_stat_date (stat_date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户活跃度统计表';
|
||||
|
||||
-- 插入2024年全年日活数据(模拟真实趋势)
|
||||
INSERT IGNORE INTO user_activity_stats (stat_date, daily_active_users, monthly_active_users, new_users, returning_users, session_count, avg_session_duration) VALUES
|
||||
-- 1月数据
|
||||
('2024-01-01', 1200, 15000, 50, 1150, 1800, 25.5),
|
||||
('2024-01-02', 1350, 15200, 45, 1305, 2000, 28.2),
|
||||
('2024-01-03', 1100, 15400, 40, 1060, 1650, 24.8),
|
||||
('2024-01-04', 1250, 15600, 55, 1195, 1900, 26.7),
|
||||
('2024-01-05', 1400, 15800, 60, 1340, 2100, 29.1),
|
||||
('2024-01-06', 1300, 16000, 50, 1250, 1950, 27.3),
|
||||
('2024-01-07', 1150, 16200, 45, 1105, 1725, 25.9),
|
||||
('2024-01-08', 1200, 16400, 55, 1145, 1800, 26.4),
|
||||
('2024-01-09', 1350, 16600, 65, 1285, 2025, 28.7),
|
||||
('2024-01-10', 1450, 16800, 70, 1380, 2175, 30.2),
|
||||
('2024-01-11', 1300, 17000, 60, 1240, 1950, 27.8),
|
||||
('2024-01-12', 1250, 17200, 55, 1195, 1875, 27.1),
|
||||
('2024-01-13', 1400, 17400, 75, 1325, 2100, 29.5),
|
||||
('2024-01-14', 1500, 17600, 80, 1420, 2250, 31.2),
|
||||
('2024-01-15', 1350, 17800, 65, 1285, 2025, 28.9),
|
||||
('2024-01-16', 1200, 18000, 50, 1150, 1800, 26.7),
|
||||
('2024-01-17', 1100, 18200, 45, 1055, 1650, 25.3),
|
||||
('2024-01-18', 1250, 18400, 60, 1190, 1875, 27.6),
|
||||
('2024-01-19', 1400, 18600, 70, 1330, 2100, 29.8),
|
||||
('2024-01-20', 1450, 18800, 75, 1375, 2175, 30.5),
|
||||
('2024-01-21', 1300, 19000, 60, 1240, 1950, 28.2),
|
||||
('2024-01-22', 1200, 19200, 55, 1145, 1800, 27.0),
|
||||
('2024-01-23', 1150, 19400, 50, 1100, 1725, 26.4),
|
||||
('2024-01-24', 1250, 19600, 65, 1185, 1875, 28.1),
|
||||
('2024-01-25', 1350, 19800, 70, 1280, 2025, 29.7),
|
||||
('2024-01-26', 1400, 20000, 75, 1325, 2100, 30.3),
|
||||
('2024-01-27', 1300, 20200, 65, 1235, 1950, 28.8),
|
||||
('2024-01-28', 1250, 20400, 60, 1190, 1875, 28.2),
|
||||
('2024-01-29', 1200, 20600, 55, 1145, 1800, 27.5),
|
||||
('2024-01-30', 1150, 20800, 50, 1100, 1725, 26.9),
|
||||
('2024-01-31', 1100, 21000, 45, 1055, 1650, 26.2),
|
||||
|
||||
-- 2月数据(春节影响,数据波动较大)
|
||||
('2024-02-01', 1000, 21200, 40, 960, 1500, 24.8),
|
||||
('2024-02-02', 950, 21400, 35, 915, 1425, 24.1),
|
||||
('2024-02-03', 900, 21600, 30, 870, 1350, 23.5),
|
||||
('2024-02-04', 850, 21800, 25, 825, 1275, 22.9),
|
||||
('2024-02-05', 800, 22000, 20, 780, 1200, 22.2),
|
||||
('2024-02-06', 750, 22200, 15, 735, 1125, 21.6),
|
||||
('2024-02-07', 700, 22400, 10, 690, 1050, 21.0),
|
||||
('2024-02-08', 650, 22600, 8, 642, 975, 20.4),
|
||||
('2024-02-09', 600, 22800, 5, 595, 900, 19.8),
|
||||
('2024-02-10', 550, 23000, 3, 547, 825, 19.2),
|
||||
('2024-02-11', 500, 23200, 2, 498, 750, 18.6),
|
||||
('2024-02-12', 450, 23400, 1, 449, 675, 18.0),
|
||||
('2024-02-13', 400, 23600, 1, 399, 600, 17.4),
|
||||
('2024-02-14', 350, 23800, 1, 349, 525, 16.8),
|
||||
('2024-02-15', 300, 24000, 1, 299, 450, 16.2),
|
||||
('2024-02-16', 250, 24200, 1, 249, 375, 15.6),
|
||||
('2024-02-17', 200, 24400, 1, 199, 300, 15.0),
|
||||
('2024-02-18', 150, 24600, 1, 149, 225, 14.4),
|
||||
('2024-02-19', 100, 24800, 1, 99, 150, 13.8),
|
||||
('2024-02-20', 80, 25000, 1, 79, 120, 13.2),
|
||||
('2024-02-21', 60, 25200, 1, 59, 90, 12.6),
|
||||
('2024-02-22', 50, 25400, 1, 49, 75, 12.0),
|
||||
('2024-02-23', 40, 25600, 1, 39, 60, 11.4),
|
||||
('2024-02-24', 30, 25800, 1, 29, 45, 10.8),
|
||||
('2024-02-25', 25, 26000, 1, 24, 37, 10.2),
|
||||
('2024-02-26', 20, 26200, 1, 19, 30, 9.6),
|
||||
('2024-02-27', 15, 26400, 1, 14, 22, 9.0),
|
||||
('2024-02-28', 10, 26600, 1, 9, 15, 8.4),
|
||||
('2024-02-29', 5, 26800, 1, 4, 7, 7.8),
|
||||
|
||||
-- 3月数据(春节后恢复)
|
||||
('2024-03-01', 200, 27000, 5, 195, 300, 12.0),
|
||||
('2024-03-02', 300, 27200, 10, 290, 450, 15.0),
|
||||
('2024-03-03', 400, 27400, 15, 385, 600, 18.0),
|
||||
('2024-03-04', 500, 27600, 20, 480, 750, 21.0),
|
||||
('2024-03-05', 600, 27800, 25, 575, 900, 24.0),
|
||||
('2024-03-06', 700, 28000, 30, 670, 1050, 27.0),
|
||||
('2024-03-07', 800, 28200, 35, 765, 1200, 30.0),
|
||||
('2024-03-08', 900, 28400, 40, 860, 1350, 33.0),
|
||||
('2024-03-09', 1000, 28600, 45, 955, 1500, 36.0),
|
||||
('2024-03-10', 1100, 28800, 50, 1050, 1650, 39.0),
|
||||
('2024-03-11', 1200, 29000, 55, 1145, 1800, 42.0),
|
||||
('2024-03-12', 1300, 29200, 60, 1240, 1950, 45.0),
|
||||
('2024-03-13', 1400, 29400, 65, 1335, 2100, 48.0),
|
||||
('2024-03-14', 1500, 29600, 70, 1430, 2250, 51.0),
|
||||
('2024-03-15', 1600, 29800, 75, 1525, 2400, 54.0),
|
||||
('2024-03-16', 1700, 30000, 80, 1620, 2550, 57.0),
|
||||
('2024-03-17', 1800, 30200, 85, 1715, 2700, 60.0),
|
||||
('2024-03-18', 1900, 30400, 90, 1810, 2850, 63.0),
|
||||
('2024-03-19', 2000, 30600, 95, 1905, 3000, 66.0),
|
||||
('2024-03-20', 2100, 30800, 100, 2000, 3150, 69.0),
|
||||
('2024-03-21', 2200, 31000, 105, 2095, 3300, 72.0),
|
||||
('2024-03-22', 2300, 31200, 110, 2190, 3450, 75.0),
|
||||
('2024-03-23', 2400, 31400, 115, 2285, 3600, 78.0),
|
||||
('2024-03-24', 2500, 31600, 120, 2380, 3750, 81.0),
|
||||
('2024-03-25', 2600, 31800, 125, 2475, 3900, 84.0),
|
||||
('2024-03-26', 2700, 32000, 130, 2570, 4050, 87.0),
|
||||
('2024-03-27', 2800, 32200, 135, 2665, 4200, 90.0),
|
||||
('2024-03-28', 2900, 32400, 140, 2760, 4350, 93.0),
|
||||
('2024-03-29', 3000, 32600, 145, 2855, 4500, 96.0),
|
||||
('2024-03-30', 3100, 32800, 150, 2950, 4650, 99.0),
|
||||
('2024-03-31', 3200, 33000, 155, 3045, 4800, 102.0),
|
||||
|
||||
-- 4月数据(春季增长期)
|
||||
('2024-04-01', 3300, 33200, 160, 3140, 4950, 105.0),
|
||||
('2024-04-02', 3400, 33400, 165, 3235, 5100, 108.0),
|
||||
('2024-04-03', 3500, 33600, 170, 3330, 5250, 111.0),
|
||||
('2024-04-04', 3600, 33800, 175, 3425, 5400, 114.0),
|
||||
('2024-04-05', 3700, 34000, 180, 3520, 5550, 117.0),
|
||||
('2024-04-06', 3800, 34200, 185, 3615, 5700, 120.0),
|
||||
('2024-04-07', 3900, 34400, 190, 3710, 5850, 123.0),
|
||||
('2024-04-08', 4000, 34600, 195, 3805, 6000, 126.0),
|
||||
('2024-04-09', 4100, 34800, 200, 3900, 6150, 129.0),
|
||||
('2024-04-10', 4200, 35000, 205, 3995, 6300, 132.0),
|
||||
('2024-04-11', 4300, 35200, 210, 4090, 6450, 135.0),
|
||||
('2024-04-12', 4400, 35400, 215, 4185, 6600, 138.0),
|
||||
('2024-04-13', 4500, 35600, 220, 4280, 6750, 141.0),
|
||||
('2024-04-14', 4600, 35800, 225, 4375, 6900, 144.0),
|
||||
('2024-04-15', 4700, 36000, 230, 4470, 7050, 147.0),
|
||||
('2024-04-16', 4800, 36200, 235, 4565, 7200, 150.0),
|
||||
('2024-04-17', 4900, 36400, 240, 4660, 7350, 153.0),
|
||||
('2024-04-18', 5000, 36600, 245, 4755, 7500, 156.0),
|
||||
('2024-04-19', 5100, 36800, 250, 4850, 7650, 159.0),
|
||||
('2024-04-20', 5200, 37000, 255, 4945, 7800, 162.0),
|
||||
('2024-04-21', 5300, 37200, 260, 5040, 7950, 165.0),
|
||||
('2024-04-22', 5400, 37400, 265, 5135, 8100, 168.0),
|
||||
('2024-04-23', 5500, 37600, 270, 5230, 8250, 171.0),
|
||||
('2024-04-24', 5600, 37800, 275, 5325, 8400, 174.0),
|
||||
('2024-04-25', 5700, 38000, 280, 5420, 8550, 177.0),
|
||||
('2024-04-26', 5800, 38200, 285, 5515, 8700, 180.0),
|
||||
('2024-04-27', 5900, 38400, 290, 5610, 8850, 183.0),
|
||||
('2024-04-28', 6000, 38600, 295, 5705, 9000, 186.0),
|
||||
('2024-04-29', 6100, 38800, 300, 5800, 9150, 189.0),
|
||||
('2024-04-30', 6200, 39000, 305, 5895, 9300, 192.0),
|
||||
|
||||
-- 5月数据(五一假期影响)
|
||||
('2024-05-01', 5000, 39200, 200, 4800, 7500, 150.0),
|
||||
('2024-05-02', 4500, 39400, 180, 4320, 6750, 135.0),
|
||||
('2024-05-03', 4000, 39600, 160, 3840, 6000, 120.0),
|
||||
('2024-05-04', 3500, 39800, 140, 3360, 5250, 105.0),
|
||||
('2024-05-05', 3000, 40000, 120, 2880, 4500, 90.0),
|
||||
('2024-05-06', 2500, 40200, 100, 2400, 3750, 75.0),
|
||||
('2024-05-07', 2000, 40400, 80, 1920, 3000, 60.0),
|
||||
('2024-05-08', 1500, 40600, 60, 1440, 2250, 45.0),
|
||||
('2024-05-09', 1000, 40800, 40, 960, 1500, 30.0),
|
||||
('2024-05-10', 800, 41000, 30, 770, 1200, 24.0),
|
||||
('2024-05-11', 600, 41200, 20, 580, 900, 18.0),
|
||||
('2024-05-12', 500, 41400, 15, 485, 750, 15.0),
|
||||
('2024-05-13', 400, 41600, 10, 390, 600, 12.0),
|
||||
('2024-05-14', 300, 41800, 8, 292, 450, 9.0),
|
||||
('2024-05-15', 250, 42000, 5, 245, 375, 7.5),
|
||||
('2024-05-16', 200, 42200, 3, 197, 300, 6.0),
|
||||
('2024-05-17', 150, 42400, 2, 148, 225, 4.5),
|
||||
('2024-05-18', 100, 42600, 1, 99, 150, 3.0),
|
||||
('2024-05-19', 80, 42800, 1, 79, 120, 2.4),
|
||||
('2024-05-20', 60, 43000, 1, 59, 90, 1.8),
|
||||
('2024-05-21', 50, 43200, 1, 49, 75, 1.5),
|
||||
('2024-05-22', 40, 43400, 1, 39, 60, 1.2),
|
||||
('2024-05-23', 30, 43600, 1, 29, 45, 0.9),
|
||||
('2024-05-24', 25, 43800, 1, 24, 37, 0.75),
|
||||
('2024-05-25', 20, 44000, 1, 19, 30, 0.6),
|
||||
('2024-05-26', 15, 44200, 1, 14, 22, 0.45),
|
||||
('2024-05-27', 10, 44400, 1, 9, 15, 0.3),
|
||||
('2024-05-28', 8, 44600, 1, 7, 12, 0.24),
|
||||
('2024-05-29', 5, 44800, 1, 4, 7, 0.15),
|
||||
('2024-05-30', 3, 45000, 1, 2, 4, 0.09),
|
||||
('2024-05-31', 2, 45200, 1, 1, 3, 0.06),
|
||||
|
||||
-- 6月数据(夏季恢复期)
|
||||
('2024-06-01', 100, 45400, 5, 95, 150, 3.0),
|
||||
('2024-06-02', 200, 45600, 10, 190, 300, 6.0),
|
||||
('2024-06-03', 300, 45800, 15, 285, 450, 9.0),
|
||||
('2024-06-04', 400, 46000, 20, 380, 600, 12.0),
|
||||
('2024-06-05', 500, 46200, 25, 475, 750, 15.0),
|
||||
('2024-06-06', 600, 46400, 30, 570, 900, 18.0),
|
||||
('2024-06-07', 700, 46600, 35, 665, 1050, 21.0),
|
||||
('2024-06-08', 800, 46800, 40, 760, 1200, 24.0),
|
||||
('2024-06-09', 900, 47000, 45, 855, 1350, 27.0),
|
||||
('2024-06-10', 1000, 47200, 50, 950, 1500, 30.0),
|
||||
('2024-06-11', 1100, 47400, 55, 1045, 1650, 33.0),
|
||||
('2024-06-12', 1200, 47600, 60, 1140, 1800, 36.0),
|
||||
('2024-06-13', 1300, 47800, 65, 1235, 1950, 39.0),
|
||||
('2024-06-14', 1400, 48000, 70, 1330, 2100, 42.0),
|
||||
('2024-06-15', 1500, 48200, 75, 1425, 2250, 45.0),
|
||||
('2024-06-16', 1600, 48400, 80, 1520, 2400, 48.0),
|
||||
('2024-06-17', 1700, 48600, 85, 1615, 2550, 51.0),
|
||||
('2024-06-18', 1800, 48800, 90, 1710, 2700, 54.0),
|
||||
('2024-06-19', 1900, 49000, 95, 1805, 2850, 57.0),
|
||||
('2024-06-20', 2000, 49200, 100, 1900, 3000, 60.0),
|
||||
('2024-06-21', 2100, 49400, 105, 1995, 3150, 63.0),
|
||||
('2024-06-22', 2200, 49600, 110, 2090, 3300, 66.0),
|
||||
('2024-06-23', 2300, 49800, 115, 2185, 3450, 69.0),
|
||||
('2024-06-24', 2400, 50000, 120, 2280, 3600, 72.0),
|
||||
('2024-06-25', 2500, 50200, 125, 2375, 3750, 75.0),
|
||||
('2024-06-26', 2600, 50400, 130, 2470, 3900, 78.0),
|
||||
('2024-06-27', 2700, 50600, 135, 2565, 4050, 81.0),
|
||||
('2024-06-28', 2800, 50800, 140, 2660, 4200, 84.0),
|
||||
('2024-06-29', 2900, 51000, 145, 2755, 4350, 87.0),
|
||||
('2024-06-30', 3000, 51200, 150, 2850, 4500, 90.0),
|
||||
|
||||
-- 7月数据(夏季高峰期)
|
||||
('2024-07-01', 3100, 51400, 155, 2945, 4650, 93.0),
|
||||
('2024-07-02', 3200, 51600, 160, 3040, 4800, 96.0),
|
||||
('2024-07-03', 3300, 51800, 165, 3135, 4950, 99.0),
|
||||
('2024-07-04', 3400, 52000, 170, 3230, 5100, 102.0),
|
||||
('2024-07-05', 3500, 52200, 175, 3325, 5250, 105.0),
|
||||
('2024-07-06', 3600, 52400, 180, 3420, 5400, 108.0),
|
||||
('2024-07-07', 3700, 52600, 185, 3515, 5550, 111.0),
|
||||
('2024-07-08', 3800, 52800, 190, 3610, 5700, 114.0),
|
||||
('2024-07-09', 3900, 53000, 195, 3705, 5850, 117.0),
|
||||
('2024-07-10', 4000, 53200, 200, 3800, 6000, 120.0),
|
||||
('2024-07-11', 4100, 53400, 205, 3895, 6150, 123.0),
|
||||
('2024-07-12', 4200, 53600, 210, 3990, 6300, 126.0),
|
||||
('2024-07-13', 4300, 53800, 215, 4085, 6450, 129.0),
|
||||
('2024-07-14', 4400, 54000, 220, 4180, 6600, 132.0),
|
||||
('2024-07-15', 4500, 54200, 225, 4275, 6750, 135.0),
|
||||
('2024-07-16', 4600, 54400, 230, 4370, 6900, 138.0),
|
||||
('2024-07-17', 4700, 54600, 235, 4465, 7050, 141.0),
|
||||
('2024-07-18', 4800, 54800, 240, 4560, 7200, 144.0),
|
||||
('2024-07-19', 4900, 55000, 245, 4655, 7350, 147.0),
|
||||
('2024-07-20', 5000, 55200, 250, 4750, 7500, 150.0),
|
||||
('2024-07-21', 5100, 55400, 255, 4845, 7650, 153.0),
|
||||
('2024-07-22', 5200, 55600, 260, 4940, 7800, 156.0),
|
||||
('2024-07-23', 5300, 55800, 265, 5035, 7950, 159.0),
|
||||
('2024-07-24', 5400, 56000, 270, 5130, 8100, 162.0),
|
||||
('2024-07-25', 5500, 56200, 275, 5225, 8250, 165.0),
|
||||
('2024-07-26', 5600, 56400, 280, 5320, 8400, 168.0),
|
||||
('2024-07-27', 5700, 56600, 285, 5415, 8550, 171.0),
|
||||
('2024-07-28', 5800, 56800, 290, 5510, 8700, 174.0),
|
||||
('2024-07-29', 5900, 57000, 295, 5605, 8850, 177.0),
|
||||
('2024-07-30', 6000, 57200, 300, 5700, 9000, 180.0),
|
||||
('2024-07-31', 6100, 57400, 305, 5795, 9150, 183.0),
|
||||
|
||||
-- 8月数据(夏季高峰延续)
|
||||
('2024-08-01', 6200, 57600, 310, 5890, 9300, 186.0),
|
||||
('2024-08-02', 6300, 57800, 315, 5985, 9450, 189.0),
|
||||
('2024-08-03', 6400, 58000, 320, 6080, 9600, 192.0),
|
||||
('2024-08-04', 6500, 58200, 325, 6175, 9750, 195.0),
|
||||
('2024-08-05', 6600, 58400, 330, 6270, 9900, 198.0),
|
||||
('2024-08-06', 6700, 58600, 335, 6365, 10050, 201.0),
|
||||
('2024-08-07', 6800, 58800, 340, 6460, 10200, 204.0),
|
||||
('2024-08-08', 6900, 59000, 345, 6555, 10350, 207.0),
|
||||
('2024-08-09', 7000, 59200, 350, 6650, 10500, 210.0),
|
||||
('2024-08-10', 7100, 59400, 355, 6745, 10650, 213.0),
|
||||
('2024-08-11', 7200, 59600, 360, 6840, 10800, 216.0),
|
||||
('2024-08-12', 7300, 59800, 365, 6935, 10950, 219.0),
|
||||
('2024-08-13', 7400, 60000, 370, 7030, 11100, 222.0),
|
||||
('2024-08-14', 7500, 60200, 375, 7125, 11250, 225.0),
|
||||
('2024-08-15', 7600, 60400, 380, 7220, 11400, 228.0),
|
||||
('2024-08-16', 7700, 60600, 385, 7315, 11550, 231.0),
|
||||
('2024-08-17', 7800, 60800, 390, 7410, 11700, 234.0),
|
||||
('2024-08-18', 7900, 61000, 395, 7505, 11850, 237.0),
|
||||
('2024-08-19', 8000, 61200, 400, 7600, 12000, 240.0),
|
||||
('2024-08-20', 8100, 61400, 405, 7695, 12150, 243.0),
|
||||
('2024-08-21', 8200, 61600, 410, 7790, 12300, 246.0),
|
||||
('2024-08-22', 8300, 61800, 415, 7885, 12450, 249.0),
|
||||
('2024-08-23', 8400, 62000, 420, 7980, 12600, 252.0),
|
||||
('2024-08-24', 8500, 62200, 425, 8075, 12750, 255.0),
|
||||
('2024-08-25', 8600, 62400, 430, 8170, 12900, 258.0),
|
||||
('2024-08-26', 8700, 62600, 435, 8265, 13050, 261.0),
|
||||
('2024-08-27', 8800, 62800, 440, 8360, 13200, 264.0),
|
||||
('2024-08-28', 8900, 63000, 445, 8455, 13350, 267.0),
|
||||
('2024-08-29', 9000, 63200, 450, 8550, 13500, 270.0),
|
||||
('2024-08-30', 9100, 63400, 455, 8645, 13650, 273.0),
|
||||
('2024-08-31', 9200, 63600, 460, 8740, 13800, 276.0),
|
||||
|
||||
-- 9月数据(秋季开学季)
|
||||
('2024-09-01', 9300, 63800, 465, 8835, 13950, 279.0),
|
||||
('2024-09-02', 9400, 64000, 470, 8930, 14100, 282.0),
|
||||
('2024-09-03', 9500, 64200, 475, 9025, 14250, 285.0),
|
||||
('2024-09-04', 9600, 64400, 480, 9120, 14400, 288.0),
|
||||
('2024-09-05', 9700, 64600, 485, 9215, 14550, 291.0),
|
||||
('2024-09-06', 9800, 64800, 490, 9310, 14700, 294.0),
|
||||
('2024-09-07', 9900, 65000, 495, 9405, 14850, 297.0),
|
||||
('2024-09-08', 10000, 65200, 500, 9500, 15000, 300.0),
|
||||
('2024-09-09', 10100, 65400, 505, 9595, 15150, 303.0),
|
||||
('2024-09-10', 10200, 65600, 510, 9690, 15300, 306.0),
|
||||
('2024-09-11', 10300, 65800, 515, 9785, 15450, 309.0),
|
||||
('2024-09-12', 10400, 66000, 520, 9880, 15600, 312.0),
|
||||
('2024-09-13', 10500, 66200, 525, 9975, 15750, 315.0),
|
||||
('2024-09-14', 10600, 66400, 530, 10070, 15900, 318.0),
|
||||
('2024-09-15', 10700, 66600, 535, 10165, 16050, 321.0),
|
||||
('2024-09-16', 10800, 66800, 540, 10260, 16200, 324.0),
|
||||
('2024-09-17', 10900, 67000, 545, 10355, 16350, 327.0),
|
||||
('2024-09-18', 11000, 67200, 550, 10450, 16500, 330.0),
|
||||
('2024-09-19', 11100, 67400, 555, 10545, 16650, 333.0),
|
||||
('2024-09-20', 11200, 67600, 560, 10640, 16800, 336.0),
|
||||
('2024-09-21', 11300, 67800, 565, 10735, 16950, 339.0),
|
||||
('2024-09-22', 11400, 68000, 570, 10830, 17100, 342.0),
|
||||
('2024-09-23', 11500, 68200, 575, 10925, 17250, 345.0),
|
||||
('2024-09-24', 11600, 68400, 580, 11020, 17400, 348.0),
|
||||
('2024-09-25', 11700, 68600, 585, 11115, 17550, 351.0),
|
||||
('2024-09-26', 11800, 68800, 590, 11210, 17700, 354.0),
|
||||
('2024-09-27', 11900, 69000, 595, 11305, 17850, 357.0),
|
||||
('2024-09-28', 12000, 69200, 600, 11400, 18000, 360.0),
|
||||
('2024-09-29', 12100, 69400, 605, 11495, 18150, 363.0),
|
||||
('2024-09-30', 12200, 69600, 610, 11590, 18300, 366.0),
|
||||
|
||||
-- 10月数据(秋季稳定期)
|
||||
('2024-10-01', 12000, 69800, 600, 11400, 18000, 360.0),
|
||||
('2024-10-02', 11800, 70000, 580, 11220, 17700, 354.0),
|
||||
('2024-10-03', 11600, 70200, 560, 11040, 17400, 348.0),
|
||||
('2024-10-04', 11400, 70400, 540, 10860, 17100, 342.0),
|
||||
('2024-10-05', 11200, 70600, 520, 10680, 16800, 336.0),
|
||||
('2024-10-06', 11000, 70800, 500, 10500, 16500, 330.0),
|
||||
('2024-10-07', 10800, 71000, 480, 10320, 16200, 324.0),
|
||||
('2024-10-08', 10600, 71200, 460, 10140, 15900, 318.0),
|
||||
('2024-10-09', 10400, 71400, 440, 9960, 15600, 312.0),
|
||||
('2024-10-10', 10200, 71600, 420, 9780, 15300, 306.0),
|
||||
('2024-10-11', 10000, 71800, 400, 9600, 15000, 300.0),
|
||||
('2024-10-12', 9800, 72000, 380, 9420, 14700, 294.0),
|
||||
('2024-10-13', 9600, 72200, 360, 9240, 14400, 288.0),
|
||||
('2024-10-14', 9400, 72400, 340, 9060, 14100, 282.0),
|
||||
('2024-10-15', 9200, 72600, 320, 8880, 13800, 276.0),
|
||||
('2024-10-16', 9000, 72800, 300, 8700, 13500, 270.0),
|
||||
('2024-10-17', 8800, 73000, 280, 8520, 13200, 264.0),
|
||||
('2024-10-18', 8600, 73200, 260, 8340, 12900, 258.0),
|
||||
('2024-10-19', 8400, 73400, 240, 8160, 12600, 252.0),
|
||||
('2024-10-20', 8200, 73600, 220, 7980, 12300, 246.0),
|
||||
('2024-10-21', 8000, 73800, 200, 7800, 12000, 240.0),
|
||||
('2024-10-22', 7800, 74000, 180, 7620, 11700, 234.0),
|
||||
('2024-10-23', 7600, 74200, 160, 7440, 11400, 228.0),
|
||||
('2024-10-24', 7400, 74400, 140, 7260, 11100, 222.0),
|
||||
('2024-10-25', 7200, 74600, 120, 7080, 10800, 216.0),
|
||||
('2024-10-26', 7000, 74800, 100, 6900, 10500, 210.0),
|
||||
('2024-10-27', 6800, 75000, 80, 6720, 10200, 204.0),
|
||||
('2024-10-28', 6600, 75200, 60, 6540, 9900, 198.0),
|
||||
('2024-10-29', 6400, 75400, 40, 6360, 9600, 192.0),
|
||||
('2024-10-30', 6200, 75600, 20, 6180, 9300, 186.0),
|
||||
('2024-10-31', 6000, 75800, 10, 5990, 9000, 180.0),
|
||||
|
||||
-- 11月数据(冬季开始)
|
||||
('2024-11-01', 5800, 76000, 5, 5795, 8700, 174.0),
|
||||
('2024-11-02', 5600, 76200, 5, 5595, 8400, 168.0),
|
||||
('2024-11-03', 5400, 76400, 5, 5395, 8100, 162.0),
|
||||
('2024-11-04', 5200, 76600, 5, 5195, 7800, 156.0),
|
||||
('2024-11-05', 5000, 76800, 5, 4995, 7500, 150.0),
|
||||
('2024-11-06', 4800, 77000, 5, 4795, 7200, 144.0),
|
||||
('2024-11-07', 4600, 77200, 5, 4595, 6900, 138.0),
|
||||
('2024-11-08', 4400, 77400, 5, 4395, 6600, 132.0),
|
||||
('2024-11-09', 4200, 77600, 5, 4195, 6300, 126.0),
|
||||
('2024-11-10', 4000, 77800, 5, 3995, 6000, 120.0),
|
||||
('2024-11-11', 3800, 78000, 5, 3795, 5700, 114.0),
|
||||
('2024-11-12', 3600, 78200, 5, 3595, 5400, 108.0),
|
||||
('2024-11-13', 3400, 78400, 5, 3395, 5100, 102.0),
|
||||
('2024-11-14', 3200, 78600, 5, 3195, 4800, 96.0),
|
||||
('2024-11-15', 3000, 78800, 5, 2995, 4500, 90.0),
|
||||
('2024-11-16', 2800, 79000, 5, 2795, 4200, 84.0),
|
||||
('2024-11-17', 2600, 79200, 5, 2595, 3900, 78.0),
|
||||
('2024-11-18', 2400, 79400, 5, 2395, 3600, 72.0),
|
||||
('2024-11-19', 2200, 79600, 5, 2195, 3300, 66.0),
|
||||
('2024-11-20', 2000, 79800, 5, 1995, 3000, 60.0),
|
||||
('2024-11-21', 1800, 80000, 5, 1795, 2700, 54.0),
|
||||
('2024-11-22', 1600, 80200, 5, 1595, 2400, 48.0),
|
||||
('2024-11-23', 1400, 80400, 5, 1395, 2100, 42.0),
|
||||
('2024-11-24', 1200, 80600, 5, 1195, 1800, 36.0),
|
||||
('2024-11-25', 1000, 80800, 5, 995, 1500, 30.0),
|
||||
('2024-11-26', 800, 81000, 5, 795, 1200, 24.0),
|
||||
('2024-11-27', 600, 81200, 5, 595, 900, 18.0),
|
||||
('2024-11-28', 400, 81400, 5, 395, 600, 12.0),
|
||||
('2024-11-29', 200, 81600, 5, 195, 300, 6.0),
|
||||
('2024-11-30', 100, 81800, 5, 95, 150, 3.0),
|
||||
|
||||
-- 12月数据(年末总结)
|
||||
('2024-12-01', 500, 82000, 10, 490, 750, 15.0),
|
||||
('2024-12-02', 600, 82200, 15, 585, 900, 18.0),
|
||||
('2024-12-03', 700, 82400, 20, 680, 1050, 21.0),
|
||||
('2024-12-04', 800, 82600, 25, 775, 1200, 24.0),
|
||||
('2024-12-05', 900, 82800, 30, 870, 1350, 27.0),
|
||||
('2024-12-06', 1000, 83000, 35, 965, 1500, 30.0),
|
||||
('2024-12-07', 1100, 83200, 40, 1060, 1650, 33.0),
|
||||
('2024-12-08', 1200, 83400, 45, 1155, 1800, 36.0),
|
||||
('2024-12-09', 1300, 83600, 50, 1250, 1950, 39.0),
|
||||
('2024-12-10', 1400, 83800, 55, 1345, 2100, 42.0),
|
||||
('2024-12-11', 1500, 84000, 60, 1440, 2250, 45.0),
|
||||
('2024-12-12', 1600, 84200, 65, 1535, 2400, 48.0),
|
||||
('2024-12-13', 1700, 84400, 70, 1630, 2550, 51.0),
|
||||
('2024-12-14', 1800, 84600, 75, 1725, 2700, 54.0),
|
||||
('2024-12-15', 1900, 84800, 80, 1820, 2850, 57.0),
|
||||
('2024-12-16', 2000, 85000, 85, 1915, 3000, 60.0),
|
||||
('2024-12-17', 2100, 85200, 90, 2010, 3150, 63.0),
|
||||
('2024-12-18', 2200, 85400, 95, 2105, 3300, 66.0),
|
||||
('2024-12-19', 2300, 85600, 100, 2200, 3450, 69.0),
|
||||
('2024-12-20', 2400, 85800, 105, 2295, 3600, 72.0),
|
||||
('2024-12-21', 2500, 86000, 110, 2390, 3750, 75.0),
|
||||
('2024-12-22', 2600, 86200, 115, 2485, 3900, 78.0),
|
||||
('2024-12-23', 2700, 86400, 120, 2580, 4050, 81.0),
|
||||
('2024-12-24', 2800, 86600, 125, 2675, 4200, 84.0),
|
||||
('2024-12-25', 2900, 86800, 130, 2770, 4350, 87.0),
|
||||
('2024-12-26', 3000, 87000, 135, 2865, 4500, 90.0),
|
||||
('2024-12-27', 3100, 87200, 140, 2960, 4650, 93.0),
|
||||
('2024-12-28', 3200, 87400, 145, 3055, 4800, 96.0),
|
||||
('2024-12-29', 3300, 87600, 150, 3150, 4950, 99.0),
|
||||
('2024-12-30', 3400, 87800, 155, 3245, 5100, 102.0),
|
||||
('2024-12-31', 3500, 88000, 160, 3340, 5250, 105.0);
|
||||
Reference in New Issue
Block a user