|
@@ -65,9 +65,6 @@ public class SalesForecastSummaryController {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private IUserClient userClient;
|
|
private IUserClient userClient;
|
|
|
|
|
|
|
|
- @Autowired
|
|
|
|
|
- private RedissonClient redissonClient; // 如需处理高并发,引入Redisson分布式锁
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 根据ID查询销售预测主表
|
|
* 根据ID查询销售预测主表
|
|
|
*/
|
|
*/
|
|
@@ -142,7 +139,7 @@ public class SalesForecastSummaryController {
|
|
|
@ApiOperation(value = "销售预测汇总添加", notes = "添加销售预测主表及明细数据,同一客户同年同月仅允许一条记录")
|
|
@ApiOperation(value = "销售预测汇总添加", notes = "添加销售预测主表及明细数据,同一客户同年同月仅允许一条记录")
|
|
|
public R<String> mainAdd(@RequestBody PcBladeSalesForecastMain pcBladeSalesForecastMain) {
|
|
public R<String> mainAdd(@RequestBody PcBladeSalesForecastMain pcBladeSalesForecastMain) {
|
|
|
try {
|
|
try {
|
|
|
- // 1. 基础参数验证(强化年份和月份的联合校验)
|
|
|
|
|
|
|
+ // 1. 基础参数验证
|
|
|
if (pcBladeSalesForecastMain == null) {
|
|
if (pcBladeSalesForecastMain == null) {
|
|
|
return R.fail("提交数据不能为空");
|
|
return R.fail("提交数据不能为空");
|
|
|
}
|
|
}
|
|
@@ -154,11 +151,9 @@ public class SalesForecastSummaryController {
|
|
|
if (month == null) {
|
|
if (month == null) {
|
|
|
return R.fail("月份为必填项");
|
|
return R.fail("月份为必填项");
|
|
|
}
|
|
}
|
|
|
- // 验证年份合理性(可根据业务调整范围,这里限制为2000-2100年)
|
|
|
|
|
if (year < 2000 || year > 2100) {
|
|
if (year < 2000 || year > 2100) {
|
|
|
return R.fail("年份必须在2000-2100之间");
|
|
return R.fail("年份必须在2000-2100之间");
|
|
|
}
|
|
}
|
|
|
- // 验证月份合法性
|
|
|
|
|
if (month < 1 || month > 12) {
|
|
if (month < 1 || month > 12) {
|
|
|
return R.fail("月份必须在1-12之间");
|
|
return R.fail("月份必须在1-12之间");
|
|
|
}
|
|
}
|
|
@@ -179,28 +174,18 @@ public class SalesForecastSummaryController {
|
|
|
}
|
|
}
|
|
|
Long customerId = userInfo.getData().getCustomerId();
|
|
Long customerId = userInfo.getData().getCustomerId();
|
|
|
|
|
|
|
|
- // 3. 分布式锁:锁键明确包含 客户ID+年份+月份(三者联合唯一)
|
|
|
|
|
- String lockKey = "sales_forecast:add:" + customerId + ":" + year + ":" + month;
|
|
|
|
|
- RLock lock = redissonClient.getLock(lockKey);
|
|
|
|
|
- try {
|
|
|
|
|
- boolean locked = lock.tryLock(5, 30, TimeUnit.SECONDS);
|
|
|
|
|
- if (!locked) {
|
|
|
|
|
- return R.fail("操作太频繁,请稍后再试");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 4. 第一次校验:检查 客户ID+年份+月份 是否已存在记录
|
|
|
|
|
|
|
+ // 3. 使用同步代码块控制并发,锁对象使用客户ID的哈希值(保证同一客户的操作串行执行)
|
|
|
|
|
+ String lockObject = "sales_forecast_lock_" + customerId;
|
|
|
|
|
+ synchronized (lockObject.intern()) {
|
|
|
|
|
+ // 4. 检查 客户ID+年份+月份 是否已存在记录
|
|
|
boolean mainExists = salesForecastMainService.checkMainDuplicate(year, month, customerId);
|
|
boolean mainExists = salesForecastMainService.checkMainDuplicate(year, month, customerId);
|
|
|
if (mainExists) {
|
|
if (mainExists) {
|
|
|
return R.fail(String.format("提交失败:%d年%d月的销售预测记录已存在", year, month));
|
|
return R.fail(String.format("提交失败:%d年%d月的销售预测记录已存在", year, month));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 5. 传递客户ID到Service,避免重复查询
|
|
|
|
|
|
|
+ // 5. 传递客户ID到Service
|
|
|
pcBladeSalesForecastMain.setCustomerId(customerId);
|
|
pcBladeSalesForecastMain.setCustomerId(customerId);
|
|
|
return salesForecastMainService.batchAdd(pcBladeSalesForecastMain);
|
|
return salesForecastMainService.batchAdd(pcBladeSalesForecastMain);
|
|
|
- } finally {
|
|
|
|
|
- if (lock.isHeldByCurrentThread()) {
|
|
|
|
|
- lock.unlock();
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|