清除数据
This commit is contained in:
@@ -6,10 +6,10 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class FlashSaleSystemApplication {
|
||||
public class CommunityFreshGroupBuySystemApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(FlashSaleSystemApplication.class, args);
|
||||
SpringApplication.run(CommunityFreshGroupBuySystemApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,16 +46,16 @@ public class GlobalExceptionHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀相关异常处理
|
||||
* 限时活动相关异常处理
|
||||
*/
|
||||
@ExceptionHandler(FlashSaleException.class)
|
||||
public ResponseEntity<ErrorResponse> handleFlashSaleException(FlashSaleException e, HttpServletRequest request) {
|
||||
log.warn("秒杀异常: {} - {}", e.getErrorCode(), e.getMessage(), e);
|
||||
log.warn("限时活动异常: {} - {}", e.getErrorCode(), e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.BAD_REQUEST.value())
|
||||
.error("Flash Sale Error")
|
||||
.error("Group Buy Error")
|
||||
.message(e.getMessage())
|
||||
.errorCode(e.getErrorCode())
|
||||
.path(request.getRequestURI())
|
||||
@@ -379,7 +379,7 @@ public class GlobalExceptionHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀异常类
|
||||
* 限时活动异常类
|
||||
*/
|
||||
public static class FlashSaleException extends RuntimeException {
|
||||
private final String errorCode;
|
||||
|
||||
@@ -32,7 +32,7 @@ public class RedisInitializer implements ApplicationRunner {
|
||||
// 1. 测试Redis连接
|
||||
testRedisConnection();
|
||||
|
||||
// 2. 预热活跃的秒杀活动库存
|
||||
// 2. 预热活跃的限时活动库存
|
||||
preloadActiveFlashSales();
|
||||
|
||||
// 3. 清理过期数据(可选)
|
||||
@@ -81,18 +81,18 @@ public class RedisInitializer implements ApplicationRunner {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热活跃的秒杀活动库存
|
||||
* 预热活跃的限时活动库存
|
||||
*/
|
||||
private void preloadActiveFlashSales() {
|
||||
try {
|
||||
log.info("正在预热活跃秒杀活动库存...");
|
||||
log.info("正在预热活跃限时活动库存...");
|
||||
|
||||
// 调用FlashSaleService的预热方法
|
||||
flashSaleService.preloadAllActiveFlashSales();
|
||||
|
||||
log.info("活跃秒杀活动库存预热完成");
|
||||
log.info("活跃限时活动库存预热完成");
|
||||
} catch (Exception e) {
|
||||
log.error("预热秒杀活动库存失败", e);
|
||||
log.error("预热限时活动库存失败", e);
|
||||
// 不抛出异常,允许应用继续启动
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,14 +234,14 @@ public class RedissonConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀Lua脚本
|
||||
* 限时活动Lua脚本
|
||||
*/
|
||||
@Bean
|
||||
public DefaultRedisScript<Long> flashSaleScript() {
|
||||
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
|
||||
script.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/flashsale.lua")));
|
||||
script.setResultType(Long.class);
|
||||
log.info("加载秒杀Lua脚本");
|
||||
log.info("加载限时活动Lua脚本");
|
||||
return script;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ public class SwaggerConfig {
|
||||
.version("1.0.0")
|
||||
.contact(new Contact()
|
||||
.name("开发团队")
|
||||
.email("dev@flashsale.com")
|
||||
.url("https://github.com/flashsale"))
|
||||
.email("dev@community-fresh-groupbuy.example")
|
||||
.url("https://github.com/community-fresh-group-buy"))
|
||||
.license(new License()
|
||||
.name("MIT License")
|
||||
.url("https://opensource.org/licenses/MIT")));
|
||||
@@ -57,12 +57,12 @@ public class SwaggerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀管理API分组
|
||||
* 限时活动管理API分组
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi flashSaleApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("秒杀管理")
|
||||
.group("限时活动管理")
|
||||
.pathsToMatch("/api/flashsale/**")
|
||||
.build();
|
||||
}
|
||||
@@ -110,4 +110,4 @@ public class SwaggerConfig {
|
||||
.pathsToMatch("/**")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,9 +137,9 @@ public class AdminController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀统计数据
|
||||
* 获取限时活动统计数据
|
||||
*/
|
||||
@Operation(summary = "获取秒杀统计数据")
|
||||
@Operation(summary = "获取限时活动统计数据")
|
||||
@GetMapping("/flashsales/stats")
|
||||
public ResponseEntity<Map<String, Object>> getFlashSaleStats() {
|
||||
try {
|
||||
@@ -147,16 +147,16 @@ public class AdminController {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "获取秒杀统计数据成功");
|
||||
response.put("message", "获取限时活动统计数据成功");
|
||||
response.put("data", stats);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取秒杀统计数据失败", e);
|
||||
log.error("获取限时活动统计数据失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "获取秒杀统计数据失败");
|
||||
response.put("message", "获取限时活动统计数据失败");
|
||||
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
|
||||
@@ -75,10 +75,10 @@ public class ApiController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃的秒杀活动
|
||||
* 获取活跃的限时活动
|
||||
*/
|
||||
@GetMapping("/flashsales/active")
|
||||
@Operation(summary = "获取活跃的秒杀活动")
|
||||
@Operation(summary = "获取活跃的限时活动")
|
||||
public ResponseEntity<Map<String, Object>> getActiveFlashSales() {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
@@ -107,19 +107,19 @@ public class ApiController {
|
||||
response.put("data", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取活跃秒杀活动失败", e);
|
||||
log.error("获取活跃限时活动失败", e);
|
||||
response.put("success", false);
|
||||
response.put("message", "获取活跃秒杀活动失败");
|
||||
response.put("message", "获取活跃限时活动失败");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参与秒杀
|
||||
* 参与限时活动
|
||||
*/
|
||||
@PostMapping("/flashsales/participate")
|
||||
@Operation(summary = "参与秒杀")
|
||||
@Operation(summary = "参与限时活动")
|
||||
public ResponseEntity<Map<String, Object>> participate(
|
||||
@RequestBody Map<String, Object> request,
|
||||
HttpServletRequest httpRequest) {
|
||||
@@ -143,8 +143,8 @@ public class ApiController {
|
||||
FlashSaleDTO.ParticipateDTO participateDTO = new FlashSaleDTO.ParticipateDTO();
|
||||
participateDTO.setFlashSaleId(flashSaleId);
|
||||
participateDTO.setQuantity(quantity);
|
||||
|
||||
// 调用秒杀服务
|
||||
|
||||
// 调用限时活动服务
|
||||
FlashSaleDTO.ResultDTO result = flashSaleService.participateFlashSale(userId, participateDTO);
|
||||
|
||||
response.put("success", result.getSuccess());
|
||||
@@ -154,7 +154,7 @@ public class ApiController {
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("参与秒杀失败", e);
|
||||
log.error("参与限时活动失败", e);
|
||||
response.put("success", false);
|
||||
response.put("message", e.getMessage());
|
||||
}
|
||||
@@ -204,10 +204,10 @@ public class ApiController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动列表
|
||||
* 获取限时活动列表
|
||||
*/
|
||||
@GetMapping("/flashsales")
|
||||
@Operation(summary = "获取秒杀活动列表")
|
||||
@Operation(summary = "获取限时活动列表")
|
||||
public ResponseEntity<Map<String, Object>> getFlashSales() {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
@@ -220,9 +220,9 @@ public class ApiController {
|
||||
response.put("total", flashSales.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取秒杀活动列表失败", e);
|
||||
log.error("获取限时活动列表失败", e);
|
||||
response.put("success", false);
|
||||
response.put("message", "获取秒杀活动列表失败");
|
||||
response.put("message", "获取限时活动列表失败");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
|
||||
@@ -22,10 +22,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 秒杀控制器
|
||||
* 处理秒杀相关的HTTP请求
|
||||
* 限时活动控制器
|
||||
* 处理限时活动相关的HTTP请求
|
||||
*/
|
||||
@Tag(name = "秒杀管理", description = "秒杀活动创建、参与、状态管理等接口")
|
||||
@Tag(name = "限时活动管理", description = "限时活动创建、参与、状态管理等接口")
|
||||
@RestController
|
||||
@RequestMapping("/api/flashsale")
|
||||
@Slf4j
|
||||
@@ -38,11 +38,11 @@ public class FlashSaleController {
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 创建秒杀活动
|
||||
* 创建限时活动
|
||||
*/
|
||||
@Operation(summary = "创建秒杀活动", description = "创建新的秒杀活动")
|
||||
@Operation(summary = "创建限时活动", description = "创建新的限时活动")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "秒杀活动创建成功"),
|
||||
@ApiResponse(responseCode = "200", description = "限时活动创建成功"),
|
||||
@ApiResponse(responseCode = "400", description = "创建失败,参数验证错误")
|
||||
})
|
||||
@PostMapping("/create")
|
||||
@@ -52,12 +52,12 @@ public class FlashSaleController {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动创建成功");
|
||||
response.put("message", "限时活动创建成功");
|
||||
response.put("data", flashSale);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("创建秒杀活动失败", e);
|
||||
log.error("创建限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -68,7 +68,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参与秒杀
|
||||
* 参与限时活动
|
||||
*/
|
||||
@PostMapping("/participate")
|
||||
public ResponseEntity<Map<String, Object>> participateFlashSale(@Validated @RequestBody FlashSaleDTO.ParticipateDTO participateDTO,
|
||||
@@ -92,7 +92,7 @@ public class FlashSaleController {
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("参与秒杀失败", e);
|
||||
log.error("参与限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -103,7 +103,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动列表
|
||||
* 获取限时活动列表
|
||||
*/
|
||||
@PostMapping("/list")
|
||||
public ResponseEntity<Map<String, Object>> getFlashSaleList(@RequestBody FlashSaleDTO.QueryDTO queryDTO) {
|
||||
@@ -116,7 +116,7 @@ public class FlashSaleController {
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取秒杀活动列表失败", e);
|
||||
log.error("获取限时活动列表失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -127,7 +127,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正在进行的秒杀活动
|
||||
* 获取正在进行的限时活动
|
||||
*/
|
||||
@GetMapping("/active")
|
||||
public ResponseEntity<Map<String, Object>> getActiveFlashSales() {
|
||||
@@ -140,7 +140,7 @@ public class FlashSaleController {
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取正在进行的秒杀活动失败", e);
|
||||
log.error("获取正在进行的限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -151,7 +151,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动统计信息
|
||||
* 获取限时活动统计信息
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
public ResponseEntity<Map<String, Object>> getFlashSaleStatistics(HttpServletRequest request) {
|
||||
@@ -165,7 +165,7 @@ public class FlashSaleController {
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取秒杀统计失败", e);
|
||||
log.error("获取限时活动统计失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -176,18 +176,18 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动详情
|
||||
* 获取限时活动详情
|
||||
*/
|
||||
@Operation(summary = "获取秒杀活动详情", description = "根据ID获取秒杀活动的详细信息")
|
||||
@Operation(summary = "获取限时活动详情", description = "根据ID获取限时活动的详细信息")
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Map<String, Object>> getFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
|
||||
public ResponseEntity<Map<String, Object>> getFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) {
|
||||
try {
|
||||
FlashSaleDTO flashSale = flashSaleService.getFlashSaleDTOById(id);
|
||||
|
||||
if (flashSale == null) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "秒杀活动不存在");
|
||||
response.put("message", "限时活动不存在");
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ public class FlashSaleController {
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取秒杀活动详情失败", e);
|
||||
log.error("获取限时活动详情失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -208,7 +208,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热所有秒杀活动库存(管理员功能)
|
||||
* 预热所有限时活动库存(管理员功能)
|
||||
*/
|
||||
@PostMapping("/admin/preload-all")
|
||||
public ResponseEntity<Map<String, Object>> preloadAllFlashSales() {
|
||||
@@ -217,11 +217,11 @@ public class FlashSaleController {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "所有秒杀活动库存预热完成");
|
||||
response.put("message", "所有限时活动库存预热完成");
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("预热所有秒杀活动库存失败", e);
|
||||
log.error("预热所有限时活动库存失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -232,28 +232,28 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新秒杀活动
|
||||
* 更新限时活动
|
||||
*/
|
||||
@Operation(summary = "更新秒杀活动", description = "更新秒杀活动信息")
|
||||
@Operation(summary = "更新限时活动", description = "更新限时活动信息")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "秒杀活动更新成功"),
|
||||
@ApiResponse(responseCode = "200", description = "限时活动更新成功"),
|
||||
@ApiResponse(responseCode = "400", description = "更新失败,参数验证错误"),
|
||||
@ApiResponse(responseCode = "404", description = "秒杀活动不存在")
|
||||
@ApiResponse(responseCode = "404", description = "限时活动不存在")
|
||||
})
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<Map<String, Object>> updateFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id,
|
||||
public ResponseEntity<Map<String, Object>> updateFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id,
|
||||
@Validated @RequestBody FlashSaleDTO.UpdateDTO updateDTO) {
|
||||
try {
|
||||
FlashSaleDTO flashSale = flashSaleService.updateFlashSale(id, updateDTO);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动更新成功");
|
||||
response.put("message", "限时活动更新成功");
|
||||
response.put("data", flashSale);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("更新秒杀活动失败", e);
|
||||
log.error("更新限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -264,26 +264,26 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除秒杀活动
|
||||
* 删除限时活动
|
||||
*/
|
||||
@Operation(summary = "删除秒杀活动", description = "删除指定的秒杀活动")
|
||||
@Operation(summary = "删除限时活动", description = "删除指定的限时活动")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "秒杀活动删除成功"),
|
||||
@ApiResponse(responseCode = "200", description = "限时活动删除成功"),
|
||||
@ApiResponse(responseCode = "400", description = "删除失败"),
|
||||
@ApiResponse(responseCode = "404", description = "秒杀活动不存在")
|
||||
@ApiResponse(responseCode = "404", description = "限时活动不存在")
|
||||
})
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Map<String, Object>> deleteFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
|
||||
public ResponseEntity<Map<String, Object>> deleteFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) {
|
||||
try {
|
||||
boolean success = flashSaleService.deleteFlashSale(id);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", success);
|
||||
response.put("message", success ? "秒杀活动删除成功" : "秒杀活动删除失败");
|
||||
response.put("message", success ? "限时活动删除成功" : "限时活动删除失败");
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("删除秒杀活动失败", e);
|
||||
log.error("删除限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -294,7 +294,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动剩余库存
|
||||
* 获取限时活动剩余库存
|
||||
*/
|
||||
@GetMapping("/{id}/stock")
|
||||
public ResponseEntity<Map<String, Object>> getFlashSaleStock(@PathVariable Long id) {
|
||||
@@ -310,7 +310,7 @@ public class FlashSaleController {
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取秒杀活动库存失败", e);
|
||||
log.error("获取限时活动库存失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -321,7 +321,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热秒杀活动
|
||||
* 预热限时活动
|
||||
*/
|
||||
@PostMapping("/{id}/preload")
|
||||
public ResponseEntity<Map<String, Object>> preloadFlashSale(@PathVariable Long id) {
|
||||
@@ -330,11 +330,11 @@ public class FlashSaleController {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动预热成功");
|
||||
response.put("message", "限时活动预热成功");
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("预热秒杀活动失败", e);
|
||||
log.error("预热限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -345,7 +345,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新秒杀活动状态(定时任务调用)
|
||||
* 更新限时活动状态(定时任务调用)
|
||||
*/
|
||||
@PostMapping("/update-status")
|
||||
public ResponseEntity<Map<String, Object>> updateFlashSaleStatus() {
|
||||
@@ -354,11 +354,11 @@ public class FlashSaleController {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动状态更新成功");
|
||||
response.put("message", "限时活动状态更新成功");
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("更新秒杀活动状态失败", e);
|
||||
log.error("更新限时活动状态失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -369,22 +369,22 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布秒杀活动
|
||||
* 发布限时活动
|
||||
*/
|
||||
@Operation(summary = "发布秒杀活动", description = "将秒杀活动状态设置为可参与")
|
||||
@Operation(summary = "发布限时活动", description = "将限时活动状态设置为可参与")
|
||||
@PostMapping("/{id}/publish")
|
||||
public ResponseEntity<Map<String, Object>> publishFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
|
||||
public ResponseEntity<Map<String, Object>> publishFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) {
|
||||
try {
|
||||
FlashSaleDTO flashSale = flashSaleService.publishFlashSale(id);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动发布成功");
|
||||
response.put("message", "限时活动发布成功");
|
||||
response.put("data", flashSale);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("发布秒杀活动失败", e);
|
||||
log.error("发布限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -395,22 +395,22 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停秒杀活动
|
||||
* 暂停限时活动
|
||||
*/
|
||||
@Operation(summary = "暂停秒杀活动", description = "暂停正在进行的秒杀活动")
|
||||
@Operation(summary = "暂停限时活动", description = "暂停正在进行的限时活动")
|
||||
@PostMapping("/{id}/pause")
|
||||
public ResponseEntity<Map<String, Object>> pauseFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
|
||||
public ResponseEntity<Map<String, Object>> pauseFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) {
|
||||
try {
|
||||
FlashSaleDTO flashSale = flashSaleService.pauseFlashSale(id);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动暂停成功");
|
||||
response.put("message", "限时活动暂停成功");
|
||||
response.put("data", flashSale);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("暂停秒杀活动失败", e);
|
||||
log.error("暂停限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -421,22 +421,22 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复秒杀活动
|
||||
* 恢复限时活动
|
||||
*/
|
||||
@Operation(summary = "恢复秒杀活动", description = "恢复已暂停的秒杀活动")
|
||||
@Operation(summary = "恢复限时活动", description = "恢复已暂停的限时活动")
|
||||
@PostMapping("/{id}/resume")
|
||||
public ResponseEntity<Map<String, Object>> resumeFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
|
||||
public ResponseEntity<Map<String, Object>> resumeFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) {
|
||||
try {
|
||||
FlashSaleDTO flashSale = flashSaleService.resumeFlashSale(id);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动恢复成功");
|
||||
response.put("message", "限时活动恢复成功");
|
||||
response.put("data", flashSale);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("恢复秒杀活动失败", e);
|
||||
log.error("恢复限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -447,22 +447,22 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束秒杀活动
|
||||
* 结束限时活动
|
||||
*/
|
||||
@Operation(summary = "结束秒杀活动", description = "提前结束秒杀活动")
|
||||
@Operation(summary = "结束限时活动", description = "提前结束限时活动")
|
||||
@PostMapping("/{id}/end")
|
||||
public ResponseEntity<Map<String, Object>> endFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
|
||||
public ResponseEntity<Map<String, Object>> endFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) {
|
||||
try {
|
||||
FlashSaleDTO flashSale = flashSaleService.endFlashSale(id);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "秒杀活动结束成功");
|
||||
response.put("message", "限时活动结束成功");
|
||||
response.put("data", flashSale);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("结束秒杀活动失败", e);
|
||||
log.error("结束限时活动失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -473,7 +473,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀压力测试接口
|
||||
* 限时活动压力测试接口
|
||||
*/
|
||||
@PostMapping("/stress-test")
|
||||
public ResponseEntity<Map<String, Object>> stressTest(@RequestParam Long flashSaleId,
|
||||
@@ -486,7 +486,7 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
// 这里可以实现压力测试逻辑
|
||||
// 模拟多个用户同时参与秒杀
|
||||
// 模拟多个用户同时参与限时活动
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
@@ -498,7 +498,7 @@ public class FlashSaleController {
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("秒杀压力测试失败", e);
|
||||
log.error("限时活动压力测试失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
@@ -509,10 +509,10 @@ public class FlashSaleController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复秒杀活动库存
|
||||
* 修复限时活动库存
|
||||
*/
|
||||
@PostMapping("/{id}/repair-stock")
|
||||
@Operation(summary = "修复秒杀库存", description = "修复指定秒杀活动的Redis库存数据")
|
||||
@Operation(summary = "修复活动库存", description = "修复指定限时活动的Redis库存数据")
|
||||
public ResponseEntity<Map<String, Object>> repairFlashSaleStock(@PathVariable Long id) {
|
||||
try {
|
||||
flashSaleService.repairFlashSaleStock(id);
|
||||
@@ -523,7 +523,7 @@ public class FlashSaleController {
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("修复秒杀库存失败", e);
|
||||
log.error("修复活动库存失败", e);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
|
||||
@@ -81,20 +81,20 @@ public class PageController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀活动列表页面
|
||||
* 限时活动列表页面
|
||||
*/
|
||||
@GetMapping("/flashsales")
|
||||
public String flashSales(Model model) {
|
||||
model.addAttribute("pageTitle", "秒杀活动");
|
||||
model.addAttribute("pageTitle", "限时活动");
|
||||
return "flashsales";
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀活动详情页面
|
||||
* 限时活动详情页面
|
||||
*/
|
||||
@GetMapping("/flashsale/{id}")
|
||||
public String flashSaleDetail(@PathVariable Long id, Model model) {
|
||||
model.addAttribute("pageTitle", "秒杀详情");
|
||||
model.addAttribute("pageTitle", "限时活动详情");
|
||||
model.addAttribute("flashSaleId", id);
|
||||
return "flashsale-detail";
|
||||
}
|
||||
@@ -179,7 +179,7 @@ public class PageController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀活动管理页面
|
||||
* 限时活动管理页面
|
||||
*/
|
||||
@GetMapping("/admin/flashsales")
|
||||
public String adminFlashSales(Model model, HttpServletRequest request) {
|
||||
@@ -187,7 +187,7 @@ public class PageController {
|
||||
return "redirect:/login?returnUrl=/admin/flashsales";
|
||||
}
|
||||
|
||||
model.addAttribute("pageTitle", "秒杀管理");
|
||||
model.addAttribute("pageTitle", "限时活动管理");
|
||||
return "admin/flashsales";
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 秒杀活动数据传输对象
|
||||
* 限时活动数据传输对象
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -42,7 +42,7 @@ public class FlashSaleDTO {
|
||||
|
||||
// 活动状态描述
|
||||
private String statusDescription;
|
||||
// 是否可以参与秒杀
|
||||
// 是否可以参与限时活动
|
||||
private Boolean canParticipate;
|
||||
// 距离开始时间(毫秒)
|
||||
private Long timeToStart;
|
||||
@@ -50,7 +50,7 @@ public class FlashSaleDTO {
|
||||
private Long timeToEnd;
|
||||
|
||||
/**
|
||||
* 创建秒杀活动DTO
|
||||
* 创建限时活动DTO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -59,11 +59,11 @@ public class FlashSaleDTO {
|
||||
@NotNull(message = "商品ID不能为空")
|
||||
private Long productId;
|
||||
|
||||
@NotNull(message = "秒杀价格不能为空")
|
||||
@DecimalMin(value = "0.01", message = "秒杀价格必须大于0")
|
||||
@NotNull(message = "活动价格不能为空")
|
||||
@DecimalMin(value = "0.01", message = "活动价格必须大于0")
|
||||
private BigDecimal flashPrice;
|
||||
|
||||
@Min(value = 1, message = "秒杀库存必须大于0")
|
||||
@Min(value = 1, message = "活动库存必须大于0")
|
||||
private Integer flashStock;
|
||||
|
||||
@NotNull(message = "开始时间不能为空")
|
||||
@@ -76,16 +76,16 @@ public class FlashSaleDTO {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新秒杀活动DTO
|
||||
* 更新限时活动DTO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class UpdateDTO {
|
||||
@DecimalMin(value = "0.01", message = "秒杀价格必须大于0")
|
||||
@DecimalMin(value = "0.01", message = "活动价格必须大于0")
|
||||
private BigDecimal flashPrice;
|
||||
|
||||
@Min(value = 1, message = "秒杀库存必须大于0")
|
||||
@Min(value = 1, message = "活动库存必须大于0")
|
||||
private Integer flashStock;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@@ -100,13 +100,13 @@ public class FlashSaleDTO {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀参与DTO
|
||||
* 限时活动参与DTO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ParticipateDTO {
|
||||
@NotNull(message = "秒杀活动ID不能为空")
|
||||
@NotNull(message = "限时活动ID不能为空")
|
||||
private Long flashSaleId;
|
||||
|
||||
@Min(value = 1, message = "购买数量必须大于0")
|
||||
@@ -118,7 +118,7 @@ public class FlashSaleDTO {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀查询DTO
|
||||
* 限时活动查询DTO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -139,7 +139,7 @@ public class FlashSaleDTO {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀结果DTO
|
||||
* 限时活动结果DTO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 秒杀活动实体类
|
||||
* 限时活动实体类
|
||||
* 对应数据库flash_sales表
|
||||
*/
|
||||
@Entity
|
||||
@@ -30,12 +30,12 @@ public class FlashSale {
|
||||
@Column(name = "product_id", nullable = false)
|
||||
private Long productId;
|
||||
|
||||
@NotNull(message = "秒杀价格不能为空")
|
||||
@DecimalMin(value = "0.01", message = "秒杀价格必须大于0")
|
||||
@NotNull(message = "活动价格不能为空")
|
||||
@DecimalMin(value = "0.01", message = "活动价格必须大于0")
|
||||
@Column(name = "flash_price", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal flashPrice;
|
||||
|
||||
@Min(value = 1, message = "秒杀库存必须大于0")
|
||||
@Min(value = 1, message = "活动库存必须大于0")
|
||||
@Column(name = "flash_stock", nullable = false)
|
||||
private Integer flashStock;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public class Order {
|
||||
private Integer status = 1;
|
||||
|
||||
/**
|
||||
* 订单类型:1-普通订单,2-秒杀订单
|
||||
* 订单类型:1-普通订单,2-限时订单
|
||||
*/
|
||||
@Column(name = "order_type", nullable = false)
|
||||
private Integer orderType = 1;
|
||||
@@ -153,7 +153,7 @@ public class Order {
|
||||
*/
|
||||
public enum OrderType {
|
||||
NORMAL(1, "普通订单"),
|
||||
FLASH_SALE(2, "秒杀订单"),
|
||||
FLASH_SALE(2, "限时订单"),
|
||||
GROUP_BUYING(3, "拼团订单");
|
||||
|
||||
private final int code;
|
||||
|
||||
@@ -13,71 +13,71 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 秒杀活动数据访问层
|
||||
* 限时活动数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface FlashSaleRepository extends JpaRepository<FlashSale, Long> {
|
||||
|
||||
/**
|
||||
* 分页查找指定商品的秒杀活动
|
||||
* 分页查找指定商品的限时活动
|
||||
*/
|
||||
Page<FlashSale> findByProductId(Long productId, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查找指定时间点覆盖的商品秒杀活动
|
||||
* 查找指定时间点覆盖的商品限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.productId = :productId AND f.startTime <= :targetTime AND f.endTime >= :targetTime")
|
||||
List<FlashSale> findByProductIdAndCoveringTime(@Param("productId") Long productId,
|
||||
@Param("targetTime") LocalDateTime targetTime);
|
||||
|
||||
/**
|
||||
* 根据商品ID和状态查找秒杀活动
|
||||
* 根据商品ID和状态查找限时活动
|
||||
*/
|
||||
Page<FlashSale> findByProductIdAndStatus(Long productId, Integer status, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查找正在进行的秒杀活动
|
||||
* 查找正在进行的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.startTime <= :now AND f.endTime > :now AND f.status = 2")
|
||||
List<FlashSale> findActiveFlashSales(@Param("now") LocalDateTime now);
|
||||
|
||||
/**
|
||||
* 分页查找正在进行的秒杀活动
|
||||
* 分页查找正在进行的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.startTime <= :now AND f.endTime > :now AND f.status = 2")
|
||||
Page<FlashSale> findActiveFlashSales(@Param("now") LocalDateTime now, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查找即将开始的秒杀活动
|
||||
* 查找即将开始的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.startTime > :now AND f.status = 1")
|
||||
List<FlashSale> findUpcomingFlashSales(@Param("now") LocalDateTime now);
|
||||
|
||||
/**
|
||||
* 分页查找即将开始的秒杀活动
|
||||
* 分页查找即将开始的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.startTime > :now AND f.status = 1")
|
||||
Page<FlashSale> findUpcomingFlashSales(@Param("now") LocalDateTime now, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查找已结束的秒杀活动
|
||||
* 查找已结束的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.endTime <= :now OR f.status = 3")
|
||||
List<FlashSale> findEndedFlashSales(@Param("now") LocalDateTime now);
|
||||
|
||||
/**
|
||||
* 分页查找已结束的秒杀活动
|
||||
* 分页查找已结束的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.endTime <= :now OR f.status = 3")
|
||||
Page<FlashSale> findEndedFlashSales(@Param("now") LocalDateTime now, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 按状态分页查找秒杀活动
|
||||
* 按状态分页查找限时活动
|
||||
*/
|
||||
Page<FlashSale> findByStatus(Integer status, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 更新秒杀库存
|
||||
* 更新活动库存
|
||||
*/
|
||||
@Modifying
|
||||
@Query("UPDATE FlashSale f SET f.flashStock = f.flashStock - :quantity WHERE f.id = :flashSaleId AND f.flashStock" +
|
||||
@@ -85,50 +85,50 @@ public interface FlashSaleRepository extends JpaRepository<FlashSale, Long> {
|
||||
int updateFlashStock(@Param("flashSaleId") Long flashSaleId, @Param("quantity") Integer quantity);
|
||||
|
||||
/**
|
||||
* 恢复秒杀库存(订单取消时使用)
|
||||
* 恢复活动库存(订单取消时使用)
|
||||
*/
|
||||
@Modifying
|
||||
@Query("UPDATE FlashSale f SET f.flashStock = f.flashStock + :quantity WHERE f.id = :flashSaleId")
|
||||
int increaseFlashStock(@Param("flashSaleId") Long flashSaleId, @Param("quantity") Integer quantity);
|
||||
|
||||
/**
|
||||
* 更新秒杀活动状态
|
||||
* 更新限时活动状态
|
||||
*/
|
||||
@Modifying
|
||||
@Query("UPDATE FlashSale f SET f.status = :status WHERE f.id = :flashSaleId")
|
||||
int updateStatus(@Param("flashSaleId") Long flashSaleId, @Param("status") Integer status);
|
||||
|
||||
/**
|
||||
* 查找指定时间范围内的秒杀活动
|
||||
* 查找指定时间范围内的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.startTime >= :startTime AND f.endTime <= :endTime")
|
||||
List<FlashSale> findByTimeRange(@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 查找有库存的正在进行的秒杀活动
|
||||
* 查找有库存的正在进行的限时活动
|
||||
*/
|
||||
@Query("SELECT f FROM FlashSale f WHERE f.startTime <= :now AND f.endTime > :now AND f.status = 2 AND f" +
|
||||
".flashStock > 0")
|
||||
List<FlashSale> findActiveFlashSalesWithStock(@Param("now") LocalDateTime now);
|
||||
|
||||
/**
|
||||
* 根据商品ID查找所有秒杀活动
|
||||
* 根据商品ID查找所有限时活动
|
||||
*/
|
||||
List<FlashSale> findByProductId(Long productId);
|
||||
|
||||
/**
|
||||
* 统计指定时间范围内正在进行的秒杀活动数量
|
||||
* 统计指定时间范围内正在进行的限时活动数量
|
||||
*/
|
||||
long countByStartTimeLessThanEqualAndEndTimeGreaterThanEqual(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 统计指定时间范围内的秒杀活动数量
|
||||
* 统计指定时间范围内的限时活动数量
|
||||
*/
|
||||
long countByStartTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 统计已结束的秒杀活动数量
|
||||
* 统计已结束的限时活动数量
|
||||
*/
|
||||
long countByEndTimeLessThan(LocalDateTime endTime);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public interface OrderRepository extends JpaRepository<Order, Long> {
|
||||
Long countByUserIdAndOrderTypeAndStatusNot5(@Param("userId") Long userId, @Param("orderType") Integer orderType);
|
||||
|
||||
/**
|
||||
* 查找秒杀订单
|
||||
* 查找限时订单
|
||||
*/
|
||||
@Query("SELECT o FROM Order o WHERE o.orderType = 2")
|
||||
List<Order> findFlashSaleOrders();
|
||||
@@ -128,18 +128,18 @@ public interface OrderRepository extends JpaRepository<Order, Long> {
|
||||
List<Order> findByUserIdAndProductId(@Param("userId") Long userId, @Param("productId") Long productId);
|
||||
|
||||
/**
|
||||
* 查找用户的秒杀订单
|
||||
* 查找用户的限时订单
|
||||
*/
|
||||
@Query("SELECT o FROM Order o WHERE o.userId = :userId AND o.orderType = 2")
|
||||
List<Order> findFlashSaleOrdersByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 检查用户是否已经参与过指定秒杀活动
|
||||
* 检查用户是否已经参与过指定限时活动
|
||||
*/
|
||||
boolean existsByUserIdAndFlashSaleIdAndOrderType(Long userId, Long flashSaleId, Integer orderType);
|
||||
|
||||
/**
|
||||
* 检查指定秒杀活动是否已有订单
|
||||
* 检查指定限时活动是否已有订单
|
||||
*/
|
||||
boolean existsByFlashSaleIdAndOrderType(Long flashSaleId, Integer orderType);
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public class AdminService {
|
||||
long totalProducts = productRepository.count();
|
||||
stats.put("totalProducts", totalProducts);
|
||||
|
||||
// 活跃秒杀数
|
||||
// 活跃限时活动数
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long activeFlashSales = flashSaleRepository.countByStartTimeLessThanEqualAndEndTimeGreaterThanEqual(now,
|
||||
now);
|
||||
@@ -272,7 +272,7 @@ public class AdminService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀统计数据
|
||||
* 获取限时活动统计数据
|
||||
*/
|
||||
public Map<String, Object> getFlashSaleStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
@@ -280,26 +280,26 @@ public class AdminService {
|
||||
try {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
// 总秒杀活动数
|
||||
// 总限时活动数
|
||||
long totalFlashSales = flashSaleRepository.count();
|
||||
stats.put("totalFlashSales", totalFlashSales);
|
||||
|
||||
// 活跃秒杀数
|
||||
// 活跃限时活动数
|
||||
long activeFlashSales = flashSaleRepository.countByStartTimeLessThanEqualAndEndTimeGreaterThanEqual(now,
|
||||
now);
|
||||
stats.put("activeFlashSales", activeFlashSales);
|
||||
|
||||
// 即将开始的秒杀数
|
||||
// 即将开始的限时活动数
|
||||
LocalDateTime oneHourLater = now.plusHours(1);
|
||||
long upcomingFlashSales = flashSaleRepository.countByStartTimeBetween(now, oneHourLater);
|
||||
stats.put("upcomingFlashSales", upcomingFlashSales);
|
||||
|
||||
// 已结束的秒杀数
|
||||
// 已结束的限时活动数
|
||||
long endedFlashSales = flashSaleRepository.countByEndTimeLessThan(now);
|
||||
stats.put("endedFlashSales", endedFlashSales);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取秒杀统计数据失败", e);
|
||||
log.error("获取限时活动统计数据失败", e);
|
||||
stats.put("totalFlashSales", 0L);
|
||||
stats.put("activeFlashSales", 0L);
|
||||
stats.put("upcomingFlashSales", 0L);
|
||||
@@ -327,7 +327,7 @@ public class AdminService {
|
||||
orderMap.put("status", order.getStatus());
|
||||
orderMap.put("createdAt", order.getCreatedAt());
|
||||
orderMap.put("orderType", order.getOrderType());
|
||||
orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示秒杀订单
|
||||
orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示限时订单
|
||||
return orderMap;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
@@ -502,7 +502,7 @@ public class AdminService {
|
||||
orderMap.put("status", order.getStatus());
|
||||
orderMap.put("createdAt", order.getCreatedAt());
|
||||
orderMap.put("orderType", order.getOrderType());
|
||||
orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示秒杀订单
|
||||
orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示限时订单
|
||||
return orderMap;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
@@ -753,7 +753,7 @@ public class AdminService {
|
||||
fileUploadService.deleteProductImage(oldImageUrl);
|
||||
}
|
||||
|
||||
// 清除引用该商品的秒杀活动缓存,确保图片/名称/价格更新及时生效
|
||||
// 清除引用该商品的限时活动缓存,确保图片/名称/价格更新及时生效
|
||||
invalidateFlashSaleCacheByProductId(id);
|
||||
} else {
|
||||
throw new RuntimeException("商品不存在");
|
||||
@@ -765,7 +765,7 @@ public class AdminService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除引用指定商品的所有秒杀活动缓存
|
||||
* 清除引用指定商品的所有限时活动缓存
|
||||
*/
|
||||
private void invalidateFlashSaleCacheByProductId(Long productId) {
|
||||
try {
|
||||
@@ -774,10 +774,10 @@ public class AdminService {
|
||||
for (com.org.flashsalesystem.entity.FlashSale fs : flashSales) {
|
||||
String cacheKey = "flashsale:" + fs.getId();
|
||||
redisService.delete(cacheKey);
|
||||
log.debug("清除秒杀活动缓存: {}", cacheKey);
|
||||
log.debug("清除限时活动缓存: {}", cacheKey);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("清除秒杀活动缓存失败: {}", e.getMessage());
|
||||
log.warn("清除限时活动缓存失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,7 +793,7 @@ public class AdminService {
|
||||
fileUploadService.deleteProductImage(product.getImageUrl());
|
||||
productService.invalidateProductCaches(id);
|
||||
productService.removeProductStockCache(id);
|
||||
// 清除关联的秒杀活动缓存
|
||||
// 清除关联的限时活动缓存
|
||||
invalidateFlashSaleCacheByProductId(id);
|
||||
productRepository.deleteById(id);
|
||||
} else {
|
||||
|
||||
@@ -30,8 +30,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 秒杀服务类
|
||||
* 实现秒杀活动管理、库存控制、分布式锁等核心功能
|
||||
* 限时活动服务类
|
||||
* 实现限时活动管理、库存控制、分布式锁等核心功能
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@@ -64,11 +64,11 @@ public class FlashSaleService {
|
||||
private int maxQuantityPerUser;
|
||||
|
||||
/**
|
||||
* 创建秒杀活动
|
||||
* 创建限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public FlashSaleDTO createFlashSale(FlashSaleDTO.CreateDTO createDTO) {
|
||||
log.info("创建秒杀活动: 商品ID={}, 秒杀价格={}, 库存={}",
|
||||
log.info("创建限时活动: 商品ID={}, 活动价格={}, 库存={}",
|
||||
createDTO.getProductId(), createDTO.getFlashPrice(), createDTO.getFlashStock());
|
||||
|
||||
// 验证商品是否存在
|
||||
@@ -87,14 +87,14 @@ public class FlashSaleService {
|
||||
throw new RuntimeException("结束时间必须晚于开始时间");
|
||||
}
|
||||
|
||||
// 创建秒杀活动
|
||||
// 创建限时活动
|
||||
FlashSale flashSale = new FlashSale();
|
||||
BeanUtils.copyProperties(createDTO, flashSale);
|
||||
flashSale.setStatus(1); // 未开始
|
||||
|
||||
flashSale = flashSaleRepository.save(flashSale);
|
||||
|
||||
// 缓存秒杀活动信息
|
||||
// 缓存限时活动信息
|
||||
cacheFlashSaleInfo(flashSale, product);
|
||||
|
||||
// 预热库存到Redis - 确保存储为数字类型
|
||||
@@ -104,7 +104,7 @@ public class FlashSaleService {
|
||||
|
||||
// 验证库存设置是否成功
|
||||
String verifyStock = redisService.getString(stockKey);
|
||||
log.info("秒杀活动库存初始化验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}",
|
||||
log.info("限时活动库存初始化验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}",
|
||||
flashSale.getId(), stockKey, flashSale.getFlashStock(), verifyStock);
|
||||
|
||||
if (!flashSale.getFlashStock().toString().equals(verifyStock)) {
|
||||
@@ -112,17 +112,17 @@ public class FlashSaleService {
|
||||
flashSale.getId(), flashSale.getFlashStock(), verifyStock);
|
||||
}
|
||||
|
||||
log.info("秒杀活动创建成功: ID={}", flashSale.getId());
|
||||
log.info("限时活动创建成功: ID={}", flashSale.getId());
|
||||
|
||||
return buildFlashSaleDTO(flashSale, product);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参与秒杀
|
||||
* 参与限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public FlashSaleDTO.ResultDTO participateFlashSale(Long userId, FlashSaleDTO.ParticipateDTO participateDTO) {
|
||||
log.info("用户参与秒杀: 用户ID={}, 秒杀ID={}, 数量={}",
|
||||
log.info("用户参与限时活动: 用户ID={}, 限时活动ID={}, 数量={}",
|
||||
userId, participateDTO.getFlashSaleId(), participateDTO.getQuantity());
|
||||
|
||||
// 限流检查
|
||||
@@ -130,26 +130,26 @@ public class FlashSaleService {
|
||||
return createFailResult("请求过于频繁,请稍后再试");
|
||||
}
|
||||
|
||||
// 获取秒杀活动信息
|
||||
// 获取限时活动信息
|
||||
FlashSale flashSale = getFlashSaleById(participateDTO.getFlashSaleId());
|
||||
if (flashSale == null) {
|
||||
return createFailResult("秒杀活动不存在");
|
||||
return createFailResult("限时活动不存在");
|
||||
}
|
||||
|
||||
// 检查活动状态
|
||||
if (!flashSale.isActive()) {
|
||||
return createFailResult("秒杀活动未开始或已结束");
|
||||
return createFailResult("限时活动未开始或已结束");
|
||||
}
|
||||
|
||||
// 检查用户是否已经参与过
|
||||
String successUsersKey = FLASH_SALE_SUCCESS_USERS_PREFIX + flashSale.getId();
|
||||
if (redisService.sIsMember(successUsersKey, userId)) {
|
||||
return createFailResult("您已经参与过该秒杀活动");
|
||||
return createFailResult("您已经参与过该限时活动");
|
||||
}
|
||||
|
||||
// 检查数据库中是否已有订单
|
||||
if (orderRepository.existsByUserIdAndFlashSaleIdAndOrderType(userId, flashSale.getId(), 2)) {
|
||||
return createFailResult("您已经参与过该秒杀活动");
|
||||
return createFailResult("您已经参与过该限时活动");
|
||||
}
|
||||
|
||||
// 检查购买数量限制
|
||||
@@ -167,16 +167,16 @@ public class FlashSaleService {
|
||||
try {
|
||||
// 二次校验:锁内重新检查用户是否已参与(防止并发竞态)
|
||||
if (redisService.sIsMember(successUsersKey, userId)) {
|
||||
return createFailResult("您已经参与过该秒杀活动");
|
||||
return createFailResult("您已经参与过该限时活动");
|
||||
}
|
||||
if (orderRepository.existsByUserIdAndFlashSaleIdAndOrderType(userId, flashSale.getId(), 2)) {
|
||||
return createFailResult("您已经参与过该秒杀活动");
|
||||
return createFailResult("您已经参与过该限时活动");
|
||||
}
|
||||
|
||||
// 检查并修复库存数据
|
||||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
|
||||
String currentStock = redisService.getString(stockKey);
|
||||
log.info("秒杀前库存检查: flashSaleId={}, stockKey={}, currentStock={}",
|
||||
log.info("限时活动前库存检查: flashSaleId={}, stockKey={}, currentStock={}",
|
||||
flashSale.getId(), stockKey, currentStock);
|
||||
|
||||
if (currentStock == null || currentStock.trim().isEmpty()) {
|
||||
@@ -203,18 +203,18 @@ public class FlashSaleService {
|
||||
currentStock = redisService.getString(stockKey);
|
||||
if (currentStock == null || currentStock.trim().isEmpty()) {
|
||||
log.error("库存数据最终验证失败: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey);
|
||||
return createFailResult("秒杀活动库存初始化失败,请稍后重试");
|
||||
return createFailResult("限时活动库存初始化失败,请稍后重试");
|
||||
}
|
||||
|
||||
// 使用Lua脚本原子性扣减库存
|
||||
log.info("准备执行秒杀脚本: stockKey={}, quantity={}, userId={}, currentStock={}",
|
||||
log.info("准备执行活动脚本: stockKey={}, quantity={}, userId={}, currentStock={}",
|
||||
stockKey, participateDTO.getQuantity(), userId, currentStock);
|
||||
Long remainingStock = redisService.executeFlashSaleScript(stockKey, participateDTO.getQuantity());
|
||||
log.info("秒杀脚本执行完成: stockKey={}, remainingStock={}", stockKey, remainingStock);
|
||||
log.info("活动脚本执行完成: stockKey={}, remainingStock={}", stockKey, remainingStock);
|
||||
|
||||
if (remainingStock < 0) {
|
||||
if (remainingStock == -1) {
|
||||
log.warn("秒杀库存key不存在或数据异常: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey);
|
||||
log.warn("活动库存key不存在或数据异常: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey);
|
||||
|
||||
// 自动修复数据并重试一次
|
||||
log.info("开始自动修复库存数据: flashSaleId={}", flashSale.getId());
|
||||
@@ -224,10 +224,10 @@ public class FlashSaleService {
|
||||
// 修复后重新验证库存
|
||||
String repairedStock = redisService.getString(stockKey);
|
||||
if (repairedStock != null && !repairedStock.trim().isEmpty()) {
|
||||
log.info("库存修复成功,重新执行秒杀脚本: flashSaleId={}, stock={}",
|
||||
log.info("库存修复成功,重新执行活动脚本: flashSaleId={}, stock={}",
|
||||
flashSale.getId(), repairedStock);
|
||||
|
||||
// 重新执行秒杀脚本
|
||||
// 重新执行活动脚本
|
||||
Long retryResult = redisService.executeFlashSaleScript(stockKey,
|
||||
participateDTO.getQuantity());
|
||||
log.info("修复后重试结果: flashSaleId={}, result={}", flashSale.getId(), retryResult);
|
||||
@@ -252,14 +252,14 @@ public class FlashSaleService {
|
||||
return createFailResult("系统繁忙,请稍后重试");
|
||||
}
|
||||
} else if (remainingStock == -2) {
|
||||
log.info("秒杀库存不足: flashSaleId={}, 剩余库存不足", flashSale.getId());
|
||||
log.info("活动库存不足: flashSaleId={}, 剩余库存不足", flashSale.getId());
|
||||
return createFailResult("商品已售罄");
|
||||
} else if (remainingStock == -3) {
|
||||
log.error("秒杀参数异常: flashSaleId={}, quantity={}", flashSale.getId(),
|
||||
log.error("限时活动参数异常: flashSaleId={}, quantity={}", flashSale.getId(),
|
||||
participateDTO.getQuantity());
|
||||
return createFailResult("参数异常,请检查购买数量");
|
||||
} else {
|
||||
log.error("秒杀脚本执行异常: flashSaleId={}, returnValue={}", flashSale.getId(), remainingStock);
|
||||
log.error("活动脚本执行异常: flashSaleId={}, returnValue={}", flashSale.getId(), remainingStock);
|
||||
return createFailResult("系统异常,请稍后重试");
|
||||
}
|
||||
}
|
||||
@@ -274,26 +274,26 @@ public class FlashSaleService {
|
||||
// 更新数据库库存
|
||||
flashSaleRepository.updateFlashStock(flashSale.getId(), participateDTO.getQuantity());
|
||||
|
||||
// 发布秒杀成功消息
|
||||
// 发布限时活动成功消息
|
||||
publishFlashSaleResult(userId, flashSale, order, true);
|
||||
|
||||
// 更新商品销量排行榜
|
||||
productService.increaseSalesRank(flashSale.getProductId(), participateDTO.getQuantity());
|
||||
|
||||
log.info("秒杀成功: 用户ID={}, 订单ID={}, 剩余库存={}", userId, order.getId(), remainingStock);
|
||||
log.info("限时活动成功: 用户ID={}, 订单ID={}, 剩余库存={}", userId, order.getId(), remainingStock);
|
||||
|
||||
return createSuccessResult(order, flashSale);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("秒杀处理异常: 用户ID={}, 秒杀ID={}", userId, participateDTO.getFlashSaleId(), e);
|
||||
return createFailResult("秒杀失败,请重试");
|
||||
log.error("限时活动处理异常: 用户ID={}, 限时活动ID={}", userId, participateDTO.getFlashSaleId(), e);
|
||||
return createFailResult("限时活动失败,请重试");
|
||||
} finally {
|
||||
redissonLockService.unlock(lockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动列表
|
||||
* 获取限时活动列表
|
||||
*/
|
||||
public Map<String, Object> getFlashSaleList(FlashSaleDTO.QueryDTO queryDTO) {
|
||||
// 验证排序字段
|
||||
@@ -360,7 +360,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正在进行的秒杀活动
|
||||
* 获取正在进行的限时活动
|
||||
*/
|
||||
public List<FlashSaleDTO> getActiveFlashSales() {
|
||||
// 尝试从缓存获取
|
||||
@@ -394,7 +394,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取秒杀活动
|
||||
* 根据ID获取限时活动
|
||||
*/
|
||||
public FlashSaleDTO getFlashSaleDTOById(Long flashSaleId) {
|
||||
FlashSale flashSale = getFlashSaleById(flashSaleId);
|
||||
@@ -407,21 +407,21 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热秒杀活动
|
||||
* 预热限时活动
|
||||
*/
|
||||
public void preloadFlashSale(Long flashSaleId) {
|
||||
log.info("预热秒杀活动: {}", flashSaleId);
|
||||
log.info("预热限时活动: {}", flashSaleId);
|
||||
|
||||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
log.warn("秒杀活动不存在: {}", flashSaleId);
|
||||
log.warn("限时活动不存在: {}", flashSaleId);
|
||||
return;
|
||||
}
|
||||
|
||||
FlashSale flashSale = flashSaleOpt.get();
|
||||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||||
|
||||
// 缓存秒杀活动信息
|
||||
// 缓存限时活动信息
|
||||
cacheFlashSaleInfo(flashSale, product);
|
||||
|
||||
// 预热库存 - 确保存储为数字类型
|
||||
@@ -431,14 +431,14 @@ public class FlashSaleService {
|
||||
|
||||
// 验证预热是否成功
|
||||
String verifyStock = redisService.getString(stockKey);
|
||||
log.info("秒杀活动预热验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}",
|
||||
log.info("限时活动预热验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}",
|
||||
flashSaleId, stockKey, flashSale.getFlashStock(), verifyStock);
|
||||
|
||||
if (!flashSale.getFlashStock().toString().equals(verifyStock)) {
|
||||
log.error("秒杀活动预热失败: flashSaleId={}, expected={}, actual={}",
|
||||
log.error("限时活动预热失败: flashSaleId={}, expected={}, actual={}",
|
||||
flashSaleId, flashSale.getFlashStock(), verifyStock);
|
||||
} else {
|
||||
log.info("秒杀活动预热完成: flashSaleId={}, stock={}", flashSaleId, verifyStock);
|
||||
log.info("限时活动预热完成: flashSaleId={}, stock={}", flashSaleId, verifyStock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,11 +446,11 @@ public class FlashSaleService {
|
||||
* 修复Redis中的库存数据
|
||||
*/
|
||||
public void repairFlashSaleStock(Long flashSaleId) {
|
||||
log.info("修复秒杀活动库存数据: {}", flashSaleId);
|
||||
log.info("修复限时活动库存数据: {}", flashSaleId);
|
||||
|
||||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
log.warn("秒杀活动不存在: {}", flashSaleId);
|
||||
log.warn("限时活动不存在: {}", flashSaleId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -501,18 +501,18 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新预热所有活跃的秒杀活动库存
|
||||
* 重新预热所有活跃的限时活动库存
|
||||
*/
|
||||
public void preloadAllActiveFlashSales() {
|
||||
log.info("开始预热所有活跃秒杀活动库存");
|
||||
log.info("开始预热所有活跃限时活动库存");
|
||||
|
||||
try {
|
||||
// 获取所有秒杀活动(包括未开始、进行中的)
|
||||
// 获取所有限时活动(包括未开始、进行中的)
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
List<FlashSale> activeFlashSales = flashSaleRepository.findAll();
|
||||
|
||||
if (activeFlashSales.isEmpty()) {
|
||||
log.info("没有找到任何秒杀活动,跳过预热");
|
||||
log.info("没有找到任何限时活动,跳过预热");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -525,13 +525,13 @@ public class FlashSaleService {
|
||||
|
||||
// 检查活动状态,只预热有效的活动
|
||||
if (flashSale.getStatus() == 3) { // 已结束的活动跳过
|
||||
log.debug("跳过已结束的秒杀活动: flashSaleId={}", flashSale.getId());
|
||||
log.debug("跳过已结束的限时活动: flashSaleId={}", flashSale.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 验证库存数据有效性
|
||||
if (flashSale.getFlashStock() == null || flashSale.getFlashStock() < 0) {
|
||||
log.warn("秒杀活动库存数据无效,跳过: flashSaleId={}, stock={}",
|
||||
log.warn("限时活动库存数据无效,跳过: flashSaleId={}, stock={}",
|
||||
flashSale.getId(), flashSale.getFlashStock());
|
||||
failCount++;
|
||||
continue;
|
||||
@@ -567,27 +567,27 @@ public class FlashSaleService {
|
||||
// 验证设置是否成功
|
||||
String verifyStock = redisService.getString(stockKey);
|
||||
if (flashSale.getFlashStock().toString().equals(verifyStock)) {
|
||||
log.info("预热秒杀活动库存成功: flashSaleId={}, stock={}, status={}",
|
||||
log.info("预热限时活动库存成功: flashSaleId={}, stock={}, status={}",
|
||||
flashSale.getId(), flashSale.getFlashStock(), getStatusText(flashSale.getStatus()));
|
||||
successCount++;
|
||||
} else {
|
||||
log.error("预热秒杀活动库存验证失败: flashSaleId={}, expected={}, actual={}",
|
||||
log.error("预热限时活动库存验证失败: flashSaleId={}, expected={}, actual={}",
|
||||
flashSale.getId(), flashSale.getFlashStock(), verifyStock);
|
||||
failCount++;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("预热秒杀活动库存失败: flashSaleId={}", flashSale.getId(), e);
|
||||
log.error("预热限时活动库存失败: flashSaleId={}", flashSale.getId(), e);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
log.info("所有秒杀活动库存预热完成: 总数={}, 成功={}, 失败={}",
|
||||
log.info("所有限时活动库存预热完成: 总数={}, 成功={}, 失败={}",
|
||||
activeFlashSales.size(), successCount, failCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("预热所有秒杀活动库存时发生异常", e);
|
||||
throw new RuntimeException("预热秒杀活动库存失败: " + e.getMessage(), e);
|
||||
log.error("预热所有限时活动库存时发生异常", e);
|
||||
throw new RuntimeException("预热限时活动库存失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,13 +611,13 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复秒杀库存(订单取消时调用)
|
||||
* 恢复活动库存(订单取消时调用)
|
||||
* 恢复Redis库存、DB库存,并移除成功用户集合记录
|
||||
*/
|
||||
@Transactional
|
||||
public void restoreFlashSaleStock(Long flashSaleId, Long productId, LocalDateTime orderCreatedAt, Long userId,
|
||||
Integer quantity) {
|
||||
log.info("恢复秒杀库存: flashSaleId={}, productId={}, orderCreatedAt={}, userId={}, quantity={}",
|
||||
log.info("恢复活动库存: flashSaleId={}, productId={}, orderCreatedAt={}, userId={}, quantity={}",
|
||||
flashSaleId, productId, orderCreatedAt, userId, quantity);
|
||||
|
||||
Optional<FlashSale> flashSaleOpt = Optional.empty();
|
||||
@@ -626,10 +626,10 @@ public class FlashSaleService {
|
||||
orderCreatedAt);
|
||||
if (matchedFlashSales.size() == 1) {
|
||||
flashSaleOpt = Optional.of(matchedFlashSales.get(0));
|
||||
log.info("根据商品和下单时间回填秒杀活动: flashSaleId={}, productId={}",
|
||||
log.info("根据商品和下单时间回填限时活动: flashSaleId={}, productId={}",
|
||||
flashSaleOpt.get().getId(), productId);
|
||||
} else {
|
||||
log.warn("订单未记录秒杀活动ID且无法唯一匹配历史活动,跳过秒杀库存恢复: productId={}, matches={}",
|
||||
log.warn("订单未记录限时活动ID且无法唯一匹配历史活动,跳过活动库存恢复: productId={}, matches={}",
|
||||
productId, matchedFlashSales.size());
|
||||
return;
|
||||
}
|
||||
@@ -638,7 +638,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
log.warn("未找到对应的秒杀活动,跳过秒杀库存恢复: flashSaleId={}", flashSaleId);
|
||||
log.warn("未找到对应的限时活动,跳过活动库存恢复: flashSaleId={}", flashSaleId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -655,12 +655,12 @@ public class FlashSaleService {
|
||||
String successUsersKey = FLASH_SALE_SUCCESS_USERS_PREFIX + flashSale.getId();
|
||||
redisService.sRem(successUsersKey, userId);
|
||||
|
||||
log.info("秒杀库存恢复成功: flashSaleId={}, userId={}, quantity={}",
|
||||
log.info("活动库存恢复成功: flashSaleId={}, userId={}, quantity={}",
|
||||
flashSale.getId(), userId, quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动剩余库存
|
||||
* 获取限时活动剩余库存
|
||||
*/
|
||||
public Integer getFlashSaleStock(Long flashSaleId) {
|
||||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId;
|
||||
@@ -669,16 +669,16 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新秒杀活动
|
||||
* 更新限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public FlashSaleDTO updateFlashSale(Long flashSaleId, FlashSaleDTO.UpdateDTO updateDTO) {
|
||||
log.info("更新秒杀活动: ID={}", flashSaleId);
|
||||
log.info("更新限时活动: ID={}", flashSaleId);
|
||||
|
||||
// 获取现有秒杀活动
|
||||
// 获取现有限时活动
|
||||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
throw new RuntimeException("秒杀活动不存在");
|
||||
throw new RuntimeException("限时活动不存在");
|
||||
}
|
||||
|
||||
FlashSale flashSale = flashSaleOpt.get();
|
||||
@@ -711,40 +711,40 @@ public class FlashSaleService {
|
||||
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
|
||||
redisService.set(stockKey, flashSale.getFlashStock());
|
||||
|
||||
log.info("秒杀活动更新成功: ID={}", flashSale.getId());
|
||||
log.info("限时活动更新成功: ID={}", flashSale.getId());
|
||||
|
||||
return buildFlashSaleDTO(flashSale, product);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除秒杀活动
|
||||
* 删除限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deleteFlashSale(Long flashSaleId) {
|
||||
log.info("删除秒杀活动: ID={}", flashSaleId);
|
||||
log.info("删除限时活动: ID={}", flashSaleId);
|
||||
|
||||
// 删除秒杀活动
|
||||
// 删除限时活动
|
||||
flashSaleRepository.deleteById(flashSaleId);
|
||||
|
||||
// 清除相关缓存
|
||||
clearFlashSaleCache(flashSaleId);
|
||||
|
||||
log.info("秒杀活动删除成功: ID={}", flashSaleId);
|
||||
log.info("限时活动删除成功: ID={}", flashSaleId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布秒杀活动
|
||||
* 发布限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public FlashSaleDTO publishFlashSale(Long flashSaleId) {
|
||||
log.info("发布秒杀活动: ID={}", flashSaleId);
|
||||
log.info("发布限时活动: ID={}", flashSaleId);
|
||||
|
||||
// 获取现有秒杀活动
|
||||
// 获取现有限时活动
|
||||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
throw new RuntimeException("秒杀活动不存在");
|
||||
throw new RuntimeException("限时活动不存在");
|
||||
}
|
||||
|
||||
FlashSale flashSale = flashSaleOpt.get();
|
||||
@@ -760,22 +760,22 @@ public class FlashSaleService {
|
||||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||||
cacheFlashSaleInfo(flashSale, product);
|
||||
|
||||
log.info("秒杀活动发布成功: ID={}", flashSaleId);
|
||||
log.info("限时活动发布成功: ID={}", flashSaleId);
|
||||
|
||||
return buildFlashSaleDTO(flashSale, product);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停秒杀活动
|
||||
* 暂停限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public FlashSaleDTO pauseFlashSale(Long flashSaleId) {
|
||||
log.info("暂停秒杀活动: ID={}", flashSaleId);
|
||||
log.info("暂停限时活动: ID={}", flashSaleId);
|
||||
|
||||
// 获取现有秒杀活动
|
||||
// 获取现有限时活动
|
||||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
throw new RuntimeException("秒杀活动不存在");
|
||||
throw new RuntimeException("限时活动不存在");
|
||||
}
|
||||
|
||||
FlashSale flashSale = flashSaleOpt.get();
|
||||
@@ -788,22 +788,22 @@ public class FlashSaleService {
|
||||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||||
cacheFlashSaleInfo(flashSale, product);
|
||||
|
||||
log.info("秒杀活动暂停成功: ID={}", flashSaleId);
|
||||
log.info("限时活动暂停成功: ID={}", flashSaleId);
|
||||
|
||||
return buildFlashSaleDTO(flashSale, product);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复秒杀活动
|
||||
* 恢复限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public FlashSaleDTO resumeFlashSale(Long flashSaleId) {
|
||||
log.info("恢复秒杀活动: ID={}", flashSaleId);
|
||||
log.info("恢复限时活动: ID={}", flashSaleId);
|
||||
|
||||
// 获取现有秒杀活动
|
||||
// 获取现有限时活动
|
||||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
throw new RuntimeException("秒杀活动不存在");
|
||||
throw new RuntimeException("限时活动不存在");
|
||||
}
|
||||
|
||||
FlashSale flashSale = flashSaleOpt.get();
|
||||
@@ -816,22 +816,22 @@ public class FlashSaleService {
|
||||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||||
cacheFlashSaleInfo(flashSale, product);
|
||||
|
||||
log.info("秒杀活动恢复成功: ID={}", flashSaleId);
|
||||
log.info("限时活动恢复成功: ID={}", flashSaleId);
|
||||
|
||||
return buildFlashSaleDTO(flashSale, product);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束秒杀活动
|
||||
* 结束限时活动
|
||||
*/
|
||||
@Transactional
|
||||
public FlashSaleDTO endFlashSale(Long flashSaleId) {
|
||||
log.info("结束秒杀活动: ID={}", flashSaleId);
|
||||
log.info("结束限时活动: ID={}", flashSaleId);
|
||||
|
||||
// 获取现有秒杀活动
|
||||
// 获取现有限时活动
|
||||
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
|
||||
if (!flashSaleOpt.isPresent()) {
|
||||
throw new RuntimeException("秒杀活动不存在");
|
||||
throw new RuntimeException("限时活动不存在");
|
||||
}
|
||||
|
||||
FlashSale flashSale = flashSaleOpt.get();
|
||||
@@ -847,13 +847,13 @@ public class FlashSaleService {
|
||||
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
|
||||
cacheFlashSaleInfo(flashSale, product);
|
||||
|
||||
log.info("秒杀活动结束成功: ID={}", flashSaleId);
|
||||
log.info("限时活动结束成功: ID={}", flashSaleId);
|
||||
|
||||
return buildFlashSaleDTO(flashSale, product);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒杀活动统计信息(即将开始、正在进行的全局数量 + 用户参与/成功数量)
|
||||
* 获取限时活动统计信息(即将开始、正在进行的全局数量 + 用户参与/成功数量)
|
||||
*/
|
||||
public Map<String, Object> getFlashSaleStatistics(Long userId) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
@@ -877,11 +877,11 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时预热即将开始的秒杀活动库存(每5分钟执行一次)
|
||||
* 定时预热即将开始的限时活动库存(每5分钟执行一次)
|
||||
*/
|
||||
@Scheduled(fixedRate = 300000)
|
||||
public void scheduledPreloadFlashSales() {
|
||||
log.info("定时任务:检查即将开始的秒杀活动并预热库存");
|
||||
log.info("定时任务:检查即将开始的限时活动并预热库存");
|
||||
try {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime threshold = now.plusMinutes(30);
|
||||
@@ -896,18 +896,18 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
if (preloadCount > 0) {
|
||||
log.info("定时预热完成:预热了{}个即将开始的秒杀活动", preloadCount);
|
||||
log.info("定时预热完成:预热了{}个即将开始的限时活动", preloadCount);
|
||||
}
|
||||
|
||||
// 同时更新秒杀活动状态
|
||||
// 同时更新限时活动状态
|
||||
updateFlashSaleStatus();
|
||||
} catch (Exception e) {
|
||||
log.error("定时预热秒杀活动失败", e);
|
||||
log.error("定时预热限时活动失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新秒杀活动状态
|
||||
* 更新限时活动状态
|
||||
*/
|
||||
@Transactional
|
||||
public void updateFlashSaleStatus() {
|
||||
@@ -918,7 +918,7 @@ public class FlashSaleService {
|
||||
for (FlashSale flashSale : upcomingFlashSales) {
|
||||
if (flashSale.isStarted() && !flashSale.isEnded()) {
|
||||
flashSaleRepository.updateStatus(flashSale.getId(), 2);
|
||||
log.info("秒杀活动开始: {}", flashSale.getId());
|
||||
log.info("限时活动开始: {}", flashSale.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,7 +927,7 @@ public class FlashSaleService {
|
||||
for (FlashSale flashSale : activeFlashSales) {
|
||||
if (flashSale.isEnded()) {
|
||||
flashSaleRepository.updateStatus(flashSale.getId(), 3);
|
||||
log.info("秒杀活动结束: {}", flashSale.getId());
|
||||
log.info("限时活动结束: {}", flashSale.getId());
|
||||
|
||||
// 清除相关缓存
|
||||
clearFlashSaleCache(flashSale.getId());
|
||||
@@ -936,7 +936,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取秒杀活动实体
|
||||
* 根据ID获取限时活动实体
|
||||
*/
|
||||
private FlashSale getFlashSaleById(Long flashSaleId) {
|
||||
// 先从缓存获取
|
||||
@@ -968,7 +968,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存秒杀活动信息
|
||||
* 缓存限时活动信息
|
||||
*/
|
||||
private void cacheFlashSaleInfo(FlashSale flashSale, Product product) {
|
||||
String cacheKey = FLASH_SALE_CACHE_PREFIX + flashSale.getId();
|
||||
@@ -991,7 +991,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建秒杀活动DTO
|
||||
* 构建限时活动DTO
|
||||
*/
|
||||
private FlashSaleDTO buildFlashSaleDTO(FlashSale flashSale, Product product) {
|
||||
FlashSaleDTO dto = new FlashSaleDTO();
|
||||
@@ -1064,7 +1064,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建秒杀订单
|
||||
* 创建限时订单
|
||||
*/
|
||||
private Order createFlashSaleOrder(Long userId, FlashSale flashSale, FlashSaleDTO.ParticipateDTO participateDTO) {
|
||||
Order order = new Order();
|
||||
@@ -1075,16 +1075,16 @@ public class FlashSaleService {
|
||||
order.setQuantity(participateDTO.getQuantity());
|
||||
order.setTotalPrice(flashSale.getFlashPrice().multiply(BigDecimal.valueOf(participateDTO.getQuantity())));
|
||||
order.setStatus(1); // 待支付
|
||||
order.setOrderType(2); // 秒杀订单
|
||||
order.setOrderType(2); // 限时订单
|
||||
order.setReceiverPhone(participateDTO.getPhone());
|
||||
order.setReceiverAddress(participateDTO.getAddress());
|
||||
order.setRemark("秒杀订单");
|
||||
order.setRemark("限时订单");
|
||||
|
||||
return orderRepository.save(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布秒杀结果消息
|
||||
* 发布限时活动结果消息
|
||||
*/
|
||||
private void publishFlashSaleResult(Long userId, FlashSale flashSale, Order order, boolean success) {
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
@@ -1104,7 +1104,7 @@ public class FlashSaleService {
|
||||
private FlashSaleDTO.ResultDTO createSuccessResult(Order order, FlashSale flashSale) {
|
||||
FlashSaleDTO.ResultDTO result = new FlashSaleDTO.ResultDTO();
|
||||
result.setSuccess(true);
|
||||
result.setMessage("秒杀成功");
|
||||
result.setMessage("限时活动成功");
|
||||
result.setOrderId(order.getId());
|
||||
result.setFlashSaleId(flashSale.getId());
|
||||
result.setProductId(flashSale.getProductId());
|
||||
@@ -1126,7 +1126,7 @@ public class FlashSaleService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除秒杀活动缓存
|
||||
* 清除限时活动缓存
|
||||
*/
|
||||
private void clearFlashSaleCache(Long flashSaleId) {
|
||||
String cacheKey = FLASH_SALE_CACHE_PREFIX + flashSaleId;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class MessageListenerService {
|
||||
new ChannelTopic("stock:change")
|
||||
);
|
||||
|
||||
// 秒杀结果监听
|
||||
// 限时活动结果监听
|
||||
redisMessageListenerContainer.addMessageListener(
|
||||
new FlashSaleResultListener(),
|
||||
new ChannelTopic("flashsale:result")
|
||||
@@ -138,26 +138,26 @@ public class MessageListenerService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理秒杀结果
|
||||
* 处理限时活动结果
|
||||
*/
|
||||
private void handleFlashSaleResult(Long userId, Long flashSaleId, Boolean success, Map<String, Object> data) {
|
||||
if (userId == null) {
|
||||
log.warn("秒杀结果缺少用户ID: flashSaleId={}", flashSaleId);
|
||||
log.warn("限时活动结果缺少用户ID: flashSaleId={}", flashSaleId);
|
||||
return;
|
||||
}
|
||||
|
||||
String link = "/flashsale/" + flashSaleId;
|
||||
|
||||
if (success) {
|
||||
String title = "秒杀成功";
|
||||
String message = "恭喜您成功抢购秒杀商品,请尽快完成支付!";
|
||||
String title = "限时活动成功";
|
||||
String message = "恭喜您成功抢购限时活动商品,请尽快完成支付!";
|
||||
notificationService.createNotification(userId, "flashsale", title, message, link);
|
||||
log.info("秒杀成功通知已创建: 用户ID={}, 秒杀ID={}", userId, flashSaleId);
|
||||
log.info("限时活动成功通知已创建: 用户ID={}, 限时活动ID={}", userId, flashSaleId);
|
||||
} else {
|
||||
String title = "秒杀未中";
|
||||
String message = "很遗憾,本次秒杀未能抢购成功,下次再来吧!";
|
||||
String title = "活动未中";
|
||||
String message = "很遗憾,本次限时活动未能抢购成功,下次再来吧!";
|
||||
notificationService.createNotification(userId, "flashsale", title, message, link);
|
||||
log.info("秒杀失败通知已创建: 用户ID={}, 秒杀ID={}", userId, flashSaleId);
|
||||
log.info("限时活动失败通知已创建: 用户ID={}, 限时活动ID={}", userId, flashSaleId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,14 +390,14 @@ public class MessageListenerService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀结果监听器
|
||||
* 限时活动结果监听器
|
||||
*/
|
||||
private class FlashSaleResultListener implements MessageListener {
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
try {
|
||||
String messageBody = new String(message.getBody());
|
||||
log.debug("接收到秒杀结果消息: {}", messageBody);
|
||||
log.debug("接收到限时活动结果消息: {}", messageBody);
|
||||
|
||||
Map<String, Object> data = parseRedissonMessage(messageBody);
|
||||
|
||||
@@ -405,13 +405,13 @@ public class MessageListenerService {
|
||||
Long flashSaleId = extractLongValue(data.get("flashSaleId"));
|
||||
Boolean success = Boolean.valueOf(data.get("success").toString());
|
||||
|
||||
log.info("秒杀结果: 用户ID={}, 秒杀ID={}, 成功={}", userId, flashSaleId, success);
|
||||
log.info("限时活动结果: 用户ID={}, 限时活动ID={}, 成功={}", userId, flashSaleId, success);
|
||||
|
||||
// 这里可以添加具体的业务处理逻辑
|
||||
handleFlashSaleResult(userId, flashSaleId, success, data);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理秒杀结果消息失败", e);
|
||||
log.error("处理限时活动结果消息失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ public class OrderService {
|
||||
// 恢复库存
|
||||
productService.updateStock(order.getProductId(), order.getQuantity(), "increase");
|
||||
|
||||
// 秒杀订单额外恢复秒杀库存
|
||||
// 限时订单额外恢复活动库存
|
||||
if (order.getOrderType() != null && order.getOrderType() == 2) {
|
||||
flashSaleService.restoreFlashSaleStock(order.getFlashSaleId(), order.getProductId(), order.getCreatedAt(),
|
||||
order.getUserId(), order.getQuantity());
|
||||
@@ -925,7 +925,7 @@ public class OrderService {
|
||||
case 1:
|
||||
return "普通订单";
|
||||
case 2:
|
||||
return "秒杀订单";
|
||||
return "限时订单";
|
||||
case 3:
|
||||
return "拼团订单";
|
||||
default:
|
||||
|
||||
@@ -57,7 +57,7 @@ public class RateLimitService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户秒杀接口限流
|
||||
* 检查用户限时活动接口限流
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*
|
||||
|
||||
@@ -369,19 +369,19 @@ public class RedisService {
|
||||
// ========== Lua脚本操作 ==========
|
||||
|
||||
/**
|
||||
* 执行秒杀脚本
|
||||
* 执行活动脚本
|
||||
*/
|
||||
public Long executeFlashSaleScript(String stockKey, int quantity) {
|
||||
log.info("执行秒杀脚本: stockKey={}, quantity={}", stockKey, quantity);
|
||||
log.info("执行活动脚本: stockKey={}, quantity={}", stockKey, quantity);
|
||||
|
||||
try {
|
||||
Long result = stringRedisTemplate.execute(flashSaleScript, Collections.singletonList(stockKey),
|
||||
String.valueOf(quantity));
|
||||
log.info("秒杀脚本执行结果: result={}", result);
|
||||
log.info("活动脚本执行结果: result={}", result);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("执行秒杀脚本异常: stockKey={}, quantity={}", stockKey, quantity, e);
|
||||
log.error("执行活动脚本异常: stockKey={}, quantity={}", stockKey, quantity, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public class JSPFunctions {
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化秒杀状态
|
||||
* 格式化限时活动状态
|
||||
*
|
||||
* @param status 状态码
|
||||
*
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
<example>${fn:formatOrderStatus(order.status)}</example>
|
||||
</function>
|
||||
|
||||
<!-- 格式化秒杀状态函数 -->
|
||||
<!-- 格式化限时活动状态函数 -->
|
||||
<function>
|
||||
<description>Format flash sale status</description>
|
||||
<name>formatFlashSaleStatus</name>
|
||||
|
||||
@@ -7,7 +7,7 @@ server:
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: flash-sale-system
|
||||
name: community-fresh-group-buy-system
|
||||
|
||||
# 数据源配置
|
||||
datasource:
|
||||
@@ -91,18 +91,18 @@ logging:
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file:
|
||||
name: logs/flash-sale-system.log
|
||||
name: logs/community-fresh-group-buy-system.log
|
||||
|
||||
# 自定义配置
|
||||
flashsale:
|
||||
# 文件上传配置
|
||||
upload:
|
||||
# 文件上传路径
|
||||
path: ${user.home}/flashsale-uploads/
|
||||
path: ${user.home}/group-buying-uploads/
|
||||
# 访问URL前缀
|
||||
url-prefix: /uploads/
|
||||
|
||||
# 秒杀配置
|
||||
# 限时活动配置
|
||||
seckill:
|
||||
# 每个用户每个商品最大购买数量
|
||||
max-quantity-per-user: 1
|
||||
@@ -129,7 +129,7 @@ flashsale:
|
||||
user-expire-minutes: 30
|
||||
# 商品信息缓存过期时间(分钟)
|
||||
product-expire-minutes: 60
|
||||
# 秒杀活动缓存过期时间(分钟)
|
||||
# 限时活动缓存过期时间(分钟)
|
||||
flashsale-expire-minutes: 10
|
||||
|
||||
# 退货配置
|
||||
@@ -143,7 +143,7 @@ flashsale:
|
||||
order-status-channel: order:status:change
|
||||
# 库存变更通知频道
|
||||
stock-change-channel: stock:change
|
||||
# 秒杀结果通知频道
|
||||
# 限时活动结果通知频道
|
||||
flashsale-result-channel: flashsale:result
|
||||
|
||||
# 监控配置
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- 秒杀Lua脚本
|
||||
-- 限时活动Lua脚本
|
||||
-- 功能:原子性地检查库存并扣减,防止超卖
|
||||
-- 参数:KEYS[1] = 库存key, ARGV[1] = 扣减数量
|
||||
-- 返回值:成功返回剩余库存,失败返回负数
|
||||
|
||||
@@ -23,10 +23,10 @@ import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 秒杀服务测试类
|
||||
* 限时活动服务测试类
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("秒杀服务测试")
|
||||
@DisplayName("限时活动服务测试")
|
||||
class FlashSaleServiceTest {
|
||||
|
||||
@Mock
|
||||
@@ -70,7 +70,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建秒杀活动 - 成功")
|
||||
@DisplayName("创建限时活动 - 成功")
|
||||
void createFlashSale_Success() {
|
||||
FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO();
|
||||
createDTO.setProductId(1L);
|
||||
@@ -92,7 +92,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建秒杀活动 - 同一商品允许多个活动")
|
||||
@DisplayName("创建限时活动 - 同一商品允许多个活动")
|
||||
void createFlashSale_MultipleActivitiesPerProductAllowed() {
|
||||
FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO();
|
||||
createDTO.setProductId(1L);
|
||||
@@ -123,7 +123,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建秒杀活动 - 商品不存在")
|
||||
@DisplayName("创建限时活动 - 商品不存在")
|
||||
void createFlashSale_ProductNotFound() {
|
||||
FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO();
|
||||
createDTO.setProductId(999L);
|
||||
@@ -139,7 +139,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建秒杀活动 - 开始时间早于当前时间")
|
||||
@DisplayName("创建限时活动 - 开始时间早于当前时间")
|
||||
void createFlashSale_StartTimeBeforeNow() {
|
||||
FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO();
|
||||
createDTO.setProductId(1L);
|
||||
@@ -159,7 +159,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取秒杀库存")
|
||||
@DisplayName("获取活动库存")
|
||||
void getFlashSaleStock_Success() {
|
||||
when(redisService.get(anyString())).thenReturn(50);
|
||||
|
||||
@@ -171,7 +171,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取秒杀库存 - 无库存数据")
|
||||
@DisplayName("获取活动库存 - 无库存数据")
|
||||
void getFlashSaleStock_NoData() {
|
||||
when(redisService.get(anyString())).thenReturn(null);
|
||||
|
||||
@@ -181,7 +181,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("预热秒杀活动")
|
||||
@DisplayName("预热限时活动")
|
||||
void preloadFlashSale_Success() {
|
||||
when(flashSaleRepository.findById(1L)).thenReturn(Optional.of(testFlashSale));
|
||||
when(productRepository.findById(1L)).thenReturn(Optional.of(testProduct));
|
||||
@@ -195,7 +195,7 @@ class FlashSaleServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("预热秒杀活动 - 活动不存在")
|
||||
@DisplayName("预热限时活动 - 活动不存在")
|
||||
void preloadFlashSale_ActivityNotFound() {
|
||||
when(flashSaleRepository.findById(999L)).thenReturn(Optional.empty());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user