feat(商城): 代码优化

master
wayn 1 year ago
parent 7bdb327605
commit 1ed26e59b0

@ -6,13 +6,14 @@ import com.wayn.common.base.controller.BaseController;
import com.wayn.common.core.domain.shop.Goods;
import com.wayn.common.core.domain.vo.GoodsSaveRelatedVO;
import com.wayn.common.core.service.shop.IGoodsService;
import com.wayn.common.enums.ReturnCodeEnum;
import com.wayn.common.exception.BusinessException;
import com.wayn.common.util.R;
import com.wayn.common.util.file.FileUtils;
import com.wayn.data.elastic.constant.EsConstants;
import com.wayn.data.elastic.manager.ElasticDocument;
import com.wayn.data.elastic.manager.ElasticEntity;
import com.wayn.data.redis.manager.RedisCache;
import com.wayn.data.redis.manager.RedisLock;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
@ -24,7 +25,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
*
@ -41,6 +41,7 @@ public class GoodsController extends BaseController {
private IGoodsService iGoodsService;
private ElasticDocument elasticDocument;
private RedisCache redisCache;
private RedisLock redisLock;
@GetMapping("/list")
public R list(Goods goods) {
@ -70,12 +71,12 @@ public class GoodsController extends BaseController {
@PostMapping("syncEs")
public R syncEs() {
if (redisCache.getCacheObject(EsConstants.ES_GOODS_INDEX_KEY) != null) {
return R.error(ReturnCodeEnum.CUSTOM_ERROR.setMsg("正在同步,请稍等"));
}
boolean flag = false;
redisCache.setCacheObject(EsConstants.ES_GOODS_INDEX_KEY, true, 3, TimeUnit.MINUTES);
try {
boolean lock = redisLock.lock(EsConstants.ES_GOODS_INDEX_KEY, 2);
if (!lock) {
throw new BusinessException("加锁失败");
}
elasticDocument.deleteIndex(EsConstants.ES_GOODS_INDEX);
InputStream inputStream = this.getClass().getResourceAsStream(EsConstants.ES_INDEX_GOODS_FILENAME);
if (elasticDocument.createIndex(EsConstants.ES_GOODS_INDEX, FileUtils.getContent(inputStream))) {
@ -100,10 +101,11 @@ public class GoodsController extends BaseController {
}
flag = elasticDocument.insertBatch(EsConstants.ES_GOODS_INDEX, entities);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
redisCache.deleteObject(EsConstants.ES_GOODS_INDEX_KEY);
redisLock.unLock(EsConstants.ES_GOODS_INDEX_KEY);
}
return R.result(flag);
}

@ -144,11 +144,6 @@
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>

@ -49,5 +49,11 @@
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
</project>

@ -1,4 +1,4 @@
package com.dogame.dragon.sparrow.framework.cache.redis;
package com.wayn.data.redis.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
@ -9,15 +9,14 @@ import org.springframework.stereotype.Component;
@Slf4j
@Component
public class LettuceConnectionValidConfig implements InitializingBean {
public class LettuceConfig implements InitializingBean {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Override
public void afterPropertiesSet() {
if (redisConnectionFactory instanceof LettuceConnectionFactory) {
LettuceConnectionFactory c = (LettuceConnectionFactory) redisConnectionFactory;
if (redisConnectionFactory instanceof LettuceConnectionFactory c) {
c.setValidateConnection(true);
}
}

@ -45,7 +45,7 @@ public class RedisCache {
* @param timeUnit
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
redisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
}
/**

@ -0,0 +1,162 @@
package com.wayn.data.redis.manager;
import cn.hutool.core.util.IdUtil;
import com.wayn.data.redis.util.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* redis
*/
@Slf4j
@Component
public class RedisLock {
@Autowired
public RedisTemplate redisTemplate;
/**
* 30
*/
public static final Integer DEFAULT_TIME_OUT = 30;
/**
* 线id-ThreadLocal
*/
private ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
/**
* watch-dog-ThreadLocal
*/
private ThreadLocal<ExecutorService> executorServiceThreadLocal = new ThreadLocal<>();
/**
*
*
* @param key key
* @return boolean
*/
public boolean lock(String key) {
return lock(key, null);
}
/**
*
*
* @param key key
* @param timeout /
* @return boolean
*/
public boolean lock(String key, Integer timeout) {
Integer timeoutTmp;
if (timeout == null) {
timeoutTmp = DEFAULT_TIME_OUT;
} else {
timeoutTmp = timeout;
}
String nanoId;
if (stringThreadLocal.get() != null) {
nanoId = stringThreadLocal.get();
} else {
nanoId = IdUtil.nanoId();
stringThreadLocal.set(nanoId);
}
RedisScript<Long> redisScript = new DefaultRedisScript<>(buildLuaLockScript(), Long.class);
Long execute = (Long) redisTemplate.execute(redisScript, Collections.singletonList(key), nanoId, timeoutTmp);
boolean flag = execute != null && execute == 1;
if (flag) {
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
executorServiceThreadLocal.set(scheduledExecutorService);
scheduledExecutorService.scheduleWithFixedDelay(() -> {
RedisScript<Long> renewRedisScript = new DefaultRedisScript<>(buildLuaRenewScript(), Long.class);
Long result = (Long) redisTemplate.execute(renewRedisScript, Collections.singletonList(key), nanoId, timeoutTmp);
if (result != null && result == 2) {
ThreadUtil.shutdownAndAwaitTermination(scheduledExecutorService);
}
}, 0, timeoutTmp / 3, TimeUnit.SECONDS);
}
return flag;
}
/**
*
*
* @param key key
* @return boolean
*/
public boolean unLock(final String key) {
String nanoId = stringThreadLocal.get();
RedisScript<Long> redisScript = new DefaultRedisScript<>(buildLuaUnLockScript(), Long.class);
Long execute = (Long) redisTemplate.execute(redisScript, Collections.singletonList(key), nanoId);
boolean flag = execute != null && execute == 1;
if (flag) {
if (executorServiceThreadLocal.get() != null) {
ThreadUtil.shutdownAndAwaitTermination(executorServiceThreadLocal.get());
}
}
return flag;
}
private String buildLuaLockScript() {
return """
local key = KEYS[1]
local value = ARGV[1]
local time_out = ARGV[2]
local result = redis.call('get', key)
if result == value then
return 1;
end
local lock_result = redis.call('setnx', key, value)
if tonumber(lock_result) == 1 then
redis.call('expire', key, time_out)
return 1;
else
return 0;
end
""";
}
private String buildLuaUnLockScript() {
return """
local key = KEYS[1]
local value = ARGV[1]
local result = redis.call('get', key)
if result ~= value then
return 0;
else
redis.call('del', key)
end
return 1;
""";
}
private String buildLuaRenewScript() {
return """
local key = KEYS[1]
local value = ARGV[1]
local timeout = ARGV[2]
local result = redis.call('get', key)
if result ~= value then
return 2;
end
local ttl = redis.call('ttl', key)
if tonumber(ttl) < tonumber(timeout) / 2 then
redis.call('expire', key, timeout)
return 1;
else
return 0;
end
""";
}
}

@ -0,0 +1,39 @@
package com.wayn.data.redis.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author: waynaqua
* @date: 2023/8/6 23:40
*/
@Slf4j
public class ThreadUtil {
/**
* 线
* 使shutdown, .
* , shutdownNow, workQueuePending,.
* 退.
* shutdown线.
*/
public static void shutdownAndAwaitTermination(ExecutorService pool) {
if (pool != null && !pool.isShutdown()) {
pool.shutdown();
try {
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
log.info("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
}

@ -25,7 +25,7 @@ public class HomeController extends BaseController {
*/
@GetMapping("index")
public R index() {
return IHomeService.getHomeIndexDataCompletableFuture();
return IHomeService.index();
}
/**

@ -60,6 +60,7 @@ public class SearchController extends BaseController {
@GetMapping("result")
public R result(SearchVO searchVO) throws IOException {
// 获取筛选、排序条件
Long memberId = MobileSecurityUtils.getUserId();
String keyword = searchVO.getKeyword();
Boolean filterNew = searchVO.getFilterNew();
@ -111,15 +112,18 @@ public class SearchController extends BaseController {
boolQueryBuilder.filter(filterQuery);
}
// 组装Elasticsearch查询条件
searchSourceBuilder.query(boolQueryBuilder);
// Elasticsearch分页相关
searchSourceBuilder.from((int) (page.getCurrent() - 1) * (int) page.getSize());
searchSourceBuilder.size((int) page.getSize());
// 执行Elasticsearch查询
List<JSONObject> list = elasticDocument.search("goods", searchSourceBuilder, JSONObject.class);
List<Integer> goodsIdList = list.stream().map(jsonObject -> (Integer) jsonObject.get("id")).collect(Collectors.toList());
if (goodsIdList.size() == 0) {
if (goodsIdList.isEmpty()) {
return R.success().add("goods", Collections.emptyList());
}
// 根据es中返回商品ID查询商品详情并保持es中的排序
// 根据Elasticsearch中返回商品ID查询商品详情并保持es中的排序
List<Goods> goodsList = iGoodsService.searchResult(goodsIdList);
Map<Integer, Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(goods -> Math.toIntExact(goods.getId()), o -> o));
List<Goods> returnGoodsList = new ArrayList<>(goodsList.size());

@ -13,7 +13,7 @@ public interface IHomeService {
* @return r
* @see <a href="https://www.cnblogs.com/cjsblog/p/9267163.html">https://www.cnblogs.com/cjsblog/p/9267163.html</a>
*/
R getHomeIndexDataCompletableFuture();
R index();
/**
*

@ -45,7 +45,7 @@ public class IHomeServiceImpl implements IHomeService {
@Override
public R getHomeIndexDataCompletableFuture() {
public R index() {
R success = R.success();
Map<String, Object> shopHomeIndexHash = redisCache.getCacheMap(SHOP_HOME_INDEX_HASH);
// 当缓存中存在数据,并且过期时间不为空而且小于等于过期时间则直接从缓存中取出数据

Loading…
Cancel
Save