购物车缓存

dev
Yunfei Du 2024-04-02 20:11:12 +08:00
parent c58caef95f
commit a896d1361f
63 changed files with 1053 additions and 99 deletions

View File

@ -0,0 +1,38 @@
package com.muyu.common.cache;
import com.muyu.common.cache.decoration.DecorationKey;
import org.springframework.stereotype.Component;
/**
*
* @author YunFei.Du
* @date 9:38 2024/4/2
*/
public interface AtomicSequenceCache<K> extends DecorationKey<K> {
/**
*
*/
public Long get(K key);
/**
*
*/
public Long increment(K key);
/**
*
*/
public Long decrement(K key);
/**
*
*/
public Long increment(K key,Long number);
/**
*
*/
public Long decrement(K key,Long number);
}

View File

@ -3,8 +3,8 @@ package com.muyu.common.cache;
import com.muyu.common.cache.decoration.DecorationKey; import com.muyu.common.cache.decoration.DecorationKey;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-26 03:25 * @Date 2024-3-26 03:25
*/ */
public interface Cache <K, V> extends DecorationKey<K> { public interface Cache <K, V> extends DecorationKey<K> {

View File

@ -7,8 +7,8 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
/** /**
* @author DongZl * dyf
* @description: Hash * Hash
* @Date 2024-3-29 03:16 * @Date 2024-3-29 03:16
*/ */
public interface HashCache <K, HK, HV> extends DecorationKey<K> { public interface HashCache <K, HK, HV> extends DecorationKey<K> {

View File

@ -0,0 +1,67 @@
package com.muyu.common.cache.abs;
import com.muyu.common.cache.AtomicSequenceCache;
import com.muyu.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* @author YunFei.Du
* @date 15:29 2024/4/2
*/
public abstract class AtomicSequenceCacheAbs<K> implements AtomicSequenceCache<K> {
@Autowired
private RedisService redisService;
/**
*
* @param key
* @return
*/
@Override
public Long get(K key) {
return this.redisService.getCacheObject ( encode ( key ) );
}
@Override
public Long increment(K key) {
return this.increment ( key,1L );
}
@Override
public Long decrement(K key) {
return this.decrement ( key,1L );
}
@Override
public Long increment(K key, Long number) {
Long numberValue = redisService.getCacheObject ( encode ( key ) );
if (numberValue==null){
Long data=getData ( key );
data= data== null ? 0L :data;
redisService.setCacheObject ( encode ( key ),data );
}
return redisService.increment(encode ( key ),number);
}
@Override
public Long decrement(K key, Long number) {
Long numberValue = redisService.getCacheObject ( encode ( key ) );
if (numberValue== null){
Long data=getData ( key );
data= data== null ? 0L :data;
redisService.setCacheObject ( encode ( key ),data );
}
return redisService.decrement(encode ( key ),number);
}
@Override
public String encode(K key) {
return keyPre ()+key;
}
public abstract Long getData(K key);
}

View File

@ -7,8 +7,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-27 03:10 * @Date 2024-3-27 03:10
*/ */
public abstract class CacheAbs<K, V> implements Cache<K, V> { public abstract class CacheAbs<K, V> implements Cache<K, V> {

View File

@ -8,8 +8,8 @@ import java.util.*;
import java.util.function.Function; import java.util.function.Function;
/** /**
* @author DongZl * dyf
* @description: hash * hash
* @Date 2024-3-29 07:40 * @Date 2024-3-29 07:40
*/ */
public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> { public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
@ -41,13 +41,25 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
} }
/** /**
* Keymap * RedisMap
* @param key * @param key Redis
* @return Map * @return MapHKHV
*/ */
@Override @Override
public Map<HK, HV> get (K key) { public Map<HK, HV> get (K key) {
return decodeMap(redisService.getCacheMap(encode(key))); // // 从Redis获取通过键编码后的缓存Map
Map< String, HV > cacheMap = redisService.getCacheMap ( encode ( key ) );
if (cacheMap==null || cacheMap.isEmpty ( )){
Map< HK, HV > dataMap = getData ( key );
if (dataMap!=null && !dataMap.isEmpty ()){
cacheMap=encodeMap ( dataMap );
}else {
cacheMap=encodeMap ( defaultValue () );
}
redisService.setCacheMap ( encode ( key ),cacheMap );
}
return decodeMap ( cacheMap );
} }
/** /**
@ -60,7 +72,13 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
*/ */
@Override @Override
public HV get (K key, HK hashKey) { public HV get (K key, HK hashKey) {
return redisService.getCacheMapValue(encode(key), encodeHashKey(hashKey)); HV hashValue = redisService.getCacheMapValue ( encode ( key ), encodeHashKey ( hashKey ) );
if (hashValue==null){
HV dataValue = getData ( key, hashKey );
hashValue= dataValue!=null ? dataValue:defaultHashValue();
put ( key,hashKey,hashValue );
}
return hashValue;
} }
/** /**
@ -73,6 +91,7 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
*/ */
@Override @Override
public List<HV> get (K key, HK... hashKeyList) { public List<HV> get (K key, HK... hashKeyList) {
// (this::encodeHashKey) ===>引用了当前类(即 this 指代的对象)中的 encodeHashKey 方法
List<String> encodeHashKeyList = Arrays.stream(hashKeyList).map(this::encodeHashKey).toList(); List<String> encodeHashKeyList = Arrays.stream(hashKeyList).map(this::encodeHashKey).toList();
return redisService.getMultiCacheMapValue(encode(key), encodeHashKeyList); return redisService.getMultiCacheMapValue(encode(key), encodeHashKeyList);
} }
@ -110,7 +129,9 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
*/ */
@Override @Override
public void put (K key, List<HV> dataList, Function<HV, HK> hashKey) { public void put (K key, List<HV> dataList, Function<HV, HK> hashKey) {
Map<HK, HV> dataMap = new HashMap<>();
dataList.forEach((data) -> dataMap.put(hashKey.apply(data), data));
redisService.setCacheMap(encode(key), encodeMap(dataMap));
} }
/** /**
@ -122,7 +143,7 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
*/ */
@Override @Override
public void put (K key, HK hashKey, HV hashValue) { public void put (K key, HK hashKey, HV hashValue) {
redisService.setCacheMapValue ( encode ( key ),encodeHashKey ( hashKey ),hashValue );
} }
/** /**
@ -132,7 +153,7 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
*/ */
@Override @Override
public void remove (K key) { public void remove (K key) {
redisService.deleteObject ( encode ( key ) );
} }
/** /**
@ -143,7 +164,7 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
*/ */
@Override @Override
public void remove (K key, HK hashKey) { public void remove (K key, HK hashKey) {
redisService.deleteCacheMapValue ( encode ( key ),encodeHashKey ( hashKey ) );
} }
/** /**
@ -158,13 +179,53 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
} }
/** /**
* *
* @param encodeDataMap * @param encodeDataMap HV
* @return * @return HKHV
*/ */
private Map<HK, HV> decodeMap(Map<String, HV> encodeDataMap){ private Map<HK, HV> decodeMap(Map<String, HV> encodeDataMap){
// 初始化一个空的HashMap用于存放解码后的键值对
Map<HK, HV> dataMap = new HashMap<>(); Map<HK, HV> dataMap = new HashMap<>();
// 遍历编码后的数据映射对每个键进行解码并将解码后的键值对添加到dataMap中
encodeDataMap.forEach((hashKey, hashValue) -> dataMap.put(decodeHashKey(hashKey), hashValue)); encodeDataMap.forEach((hashKey, hashValue) -> dataMap.put(decodeHashKey(hashKey), hashValue));
return dataMap; return dataMap;
} }
// getData
/**
*
*
* @param key
* @return
*/
public abstract Map<HK,HV> getData(K key);
/**
*
*
* @param key
* @param hashKey
* @return
*/
public abstract HV getData(K key,HK hashKey);
/**
*
* Map
*
* @return <HK, HV>
*/
public abstract Map<HK,HV> defaultValue();
/**
*
* HV
*
* @return HV
*/
public abstract HV defaultHashValue();
public boolean hasKey(K key, HK hashKey){
return redisService.hasKey ( encode ( key ),encodeHashKey ( hashKey ) );
}
} }

View File

@ -1,8 +1,8 @@
package com.muyu.common.cache.decoration; package com.muyu.common.cache.decoration;
/** /**
* @author DongZl * dyf
* @description: Key * Key
* @Date 2024-3-29 03:19 * @Date 2024-3-29 03:19
*/ */
public interface DecorationKey <K>{ public interface DecorationKey <K>{

View File

@ -5,8 +5,8 @@ import org.apache.commons.lang3.ObjectUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-10-9 04:56 * @Date 2023-10-9 04:56
*/ */
public class ObjUtils { public class ObjUtils {

View File

@ -11,16 +11,28 @@ import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* spring redis * spring redis
* * @author YunFei.Du
* @author muyu * @date 10:08 2024/4/2
**/ */
@SuppressWarnings(value = {"unchecked", "rawtypes"}) @SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component @Component
public class RedisService { public class RedisService {
@Autowired @Autowired
public RedisTemplate redisTemplate; public RedisTemplate redisTemplate;
/**
* redis hashKey
* @param key
* @param hashKey
* @return
*/
public Boolean hasKey(final String key,final String hashKey){
return this.redisTemplate.opsForHash ().hasKey ( key,hashKey );
}
/** /**
* IntegerString * IntegerString
* *
@ -233,6 +245,7 @@ public class RedisService {
return redisTemplate.opsForHash().multiGet(key, hKeys); return redisTemplate.opsForHash().multiGet(key, hKeys);
} }
/** /**
* Hash * Hash
* *
@ -255,4 +268,14 @@ public class RedisService {
public Collection<String> keys (final String pattern) { public Collection<String> keys (final String pattern) {
return redisTemplate.keys(pattern); return redisTemplate.keys(pattern);
} }
public Long increment(String encode, Long number) {
return redisTemplate.opsForValue ().increment ( encode,number );
}
public Long decrement(String encode, Long number) {
return redisTemplate.opsForValue ().decrement ( encode,number );
}
} }

View File

@ -6,8 +6,8 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-11-12 03:36 * @Date 2023-11-12 03:36
*/ */
@Data @Data

View File

@ -14,13 +14,13 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
/** /**
* @author DongZl *
* @description:
* @Date 2024-3-27 03:30 * @Date 2024-3-27 03:30
*/ */
@Component @Component
public class ProjectInfoCache extends CacheAbs<Long, ProjectInfo> { public class ProjectInfoCache extends CacheAbs<Long, ProjectInfo> {
public static void main (String[] args) { public static void main (String[] args) {
Long projectId = 10L; Long projectId = 10L;
HashCache<Long, Long, ProjectSkuInfo> hashCache = null; HashCache<Long, Long, ProjectSkuInfo> hashCache = null;
@ -33,12 +33,12 @@ public class ProjectInfoCache extends CacheAbs<Long, ProjectInfo> {
hashCache.get(projectId, longArr); hashCache.get(projectId, longArr);
} }
@Autowired @Autowired
private ProjectInfoData projectInfoData; private ProjectInfoData projectInfoData;
/** /**
* key * key
*
* @return key * @return key
*/ */
@Override @Override
@ -57,7 +57,6 @@ public class ProjectInfoCache extends CacheAbs<Long, ProjectInfo> {
public Long decode (String redisKey) { public Long decode (String redisKey) {
return Convert.toLong(redisKey.replace(keyPre(),""), 0L); return Convert.toLong(redisKey.replace(keyPre(),""), 0L);
} }
/** /**
* *
* *
@ -65,6 +64,7 @@ public class ProjectInfoCache extends CacheAbs<Long, ProjectInfo> {
* *
* @return * @return
*/ */
@Override @Override
public ProjectInfo getData (Long key) { public ProjectInfo getData (Long key) {
return projectInfoData.getData(key); return projectInfoData.getData(key);
@ -72,6 +72,10 @@ public class ProjectInfoCache extends CacheAbs<Long, ProjectInfo> {
public ProjectInfo defaultValue (Long key) {
return projectInfoData.getData(key);
}
/** /**
* *

View File

@ -1,9 +1,70 @@
package com.muyu.product.cache; package com.muyu.product.cache;
import com.muyu.common.cache.abs.HashCacheAbs;
import com.muyu.common.core.text.Convert;
import com.muyu.product.cache.datasource.ProjectSkuData;
import com.muyu.product.domain.ProjectSkuInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/** /**
* @author DongZl * sku
* @description: sku
* @Date 2024-3-29 03:06 * @Date 2024-3-29 03:06
*/ */
public class ProjectSkuCache {
@Component
public class ProjectSkuCache extends HashCacheAbs<Long,String, ProjectSkuInfo > {
@Autowired
private ProjectSkuData projectSkuData;
@Override
public String keyPre() {
return "project:sku:";
}
/**
* RedisLong
*
* @param redisKey Redis便
* @return LongredisKey0L
*/
@Override
public Long decode(String redisKey) {
// 使用Convert工具类将处理过的Redis键转换为Long类型数值原始键通过调用keyPre()方法移除了前缀
// 若转换过程中出现异常或无法转换时返回默认值0L
return Convert.toLong ( redisKey.replace ( keyPre (),"" ),0L );
}
@Override
public String decodeHashKey(String redisHashKey) {
return Convert.toStr ( redisHashKey, "" );
}
@Override
public Map< String, ProjectSkuInfo > getData(Long key) {
return projectSkuData.getData ( key );
}
@Override
public ProjectSkuInfo getData(Long key, String hashKey) {
return projectSkuData.getData ( key,hashKey );
}
@Override
public Map< String, ProjectSkuInfo > defaultValue() {
return new HashMap<> ();
}
@Override
public ProjectSkuInfo defaultHashValue() {
return new ProjectSkuInfo ();
}
} }

View File

@ -1,9 +1,62 @@
package com.muyu.product.cache; package com.muyu.product.cache;
import com.github.pagehelper.PageHelper;
import com.muyu.common.cache.abs.AtomicSequenceCacheAbs;
import com.muyu.common.cache.abs.HashCacheAbs;
import com.muyu.common.core.text.Convert;
import com.muyu.product.cache.datasource.ProjectSkuData;
import com.muyu.product.cache.datasource.ProjectSkuStockData;
import com.muyu.product.cache.key.SkuStockKey;
import com.muyu.product.domain.ProjectSkuInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/** /**
* @author DongZl * dyf
* @description: SKU * SKU
* @Date 2024-3-29 03:06 * @Date 2024-3-29 03:06
*/ */
public class ProjectSkuStockCache { @Component
public class ProjectSkuStockCache extends AtomicSequenceCacheAbs< SkuStockKey > {
@Autowired
private ProjectSkuStockData projectSkuStockData;
@Override
public String keyPre() {
return "project:sku:stock";
}
/**
*
* @param skuStockKey ID
* @return
*/
@Override
public String encode(SkuStockKey skuStockKey) {
return keyPre ()+skuStockKey.getProjectId ()+":"+skuStockKey.getSku ();
}
/**
*
* @param redisKey
* @return ID
*/
@Override
public SkuStockKey decode(String redisKey) {
String[] split = redisKey.replace ( keyPre ( ), "" ).split ( ":" );
return SkuStockKey.builder ()
.projectId ( Convert.toLong ( split[0] ) )
.sku ( split[1] )
.build ();
}
@Override
public Long getData(SkuStockKey key) {
return projectSkuStockData.getData ( key );
}
} }

View File

@ -3,8 +3,7 @@ package com.muyu.product.cache.datasource;
import com.muyu.product.domain.ProjectInfo; import com.muyu.product.domain.ProjectInfo;
/** /**
* @author DongZl *
* @description:
* @Date 2024-3-27 03:34 * @Date 2024-3-27 03:34
*/ */
public interface ProjectInfoData { public interface ProjectInfoData {
@ -16,5 +15,4 @@ public interface ProjectInfoData {
*/ */
public ProjectInfo getData (Long key); public ProjectInfo getData (Long key);
} }

View File

@ -0,0 +1,30 @@
package com.muyu.product.cache.datasource;
import com.muyu.product.domain.ProjectSkuInfo;
import java.util.Map;
/**
* SKU
* @author YunFei.Du
* @date 14:14 2024/4/1
*/
public interface ProjectSkuData {
/**
* hash
* @param projectId ID
* @return
*/
public Map<String, ProjectSkuInfo > getData (Long projectId) ;
/**
* hashhash
* @param projectId ID
* @param projectSku SKU
*
* @return hash
*/
public ProjectSkuInfo getData (Long projectId, String projectSku);
}

View File

@ -0,0 +1,16 @@
package com.muyu.product.cache.datasource;
import com.muyu.product.cache.key.SkuStockKey;
import org.springframework.stereotype.Service;
/**
* SKU
* @author YunFei.Du
* @date 16:07 2024/4/2
*/
public interface ProjectSkuStockData {
public Long getData (SkuStockKey key);
}

View File

@ -0,0 +1,28 @@
package com.muyu.product.cache.key;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName SkuStockKey
* @Description
* @Author YunFei.Du
* @Date 2024/4/2 16:08
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SkuStockKey {
/**
* id
*/
private Long projectId;
/**
* sku
*/
private String sku;
}

View File

@ -0,0 +1,3 @@
com.muyu.product.cache.ProjectInfoCache
com.muyu.product.cache.ProjectSkuCache
com.muyu.product.cache.ProjectSkuStockCache

View File

@ -1,8 +1,8 @@
package com.muyu.product.domain.base; package com.muyu.product.domain.base;
/** /**
* @author DongZl * dyf
* @description: attribute * attribute
* @Date 2024-3-1 02:28 * @Date 2024-3-1 02:28
*/ */
public interface CategoryBase { public interface CategoryBase {

View File

@ -6,8 +6,8 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-22 10:53 * @Date 2024-3-22 10:53
*/ */

View File

@ -14,8 +14,8 @@ import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-2-28 03:16 * @Date 2024-2-28 03:16
*/ */
@Data @Data

View File

@ -10,8 +10,8 @@ import lombok.NoArgsConstructor;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
* @author DongZl * dyf
* @description: Sku * Sku
* @Date 2024-3-22 10:54 * @Date 2024-3-22 10:54
*/ */
@Data @Data

View File

@ -6,8 +6,8 @@ import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-22 10:50 * @Date 2024-3-22 10:50
*/ */
@Data @Data

View File

@ -14,8 +14,8 @@ import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-4 02:28 * @Date 2024-3-4 02:28
*/ */
@Data @Data

View File

@ -12,8 +12,8 @@ import lombok.experimental.SuperBuilder;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-4 02:33 * @Date 2024-3-4 02:33
*/ */
@Data @Data

View File

@ -14,8 +14,8 @@ import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-6 02:29 * @Date 2024-3-6 02:29
*/ */
@Data @Data

View File

@ -9,8 +9,8 @@ import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-6 02:30 * @Date 2024-3-6 02:30
*/ */
@Data @Data

View File

@ -13,8 +13,8 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-2-28 04:15 * @Date 2024-2-28 04:15
*/ */
@Data @Data

View File

@ -9,8 +9,8 @@ import lombok.experimental.SuperBuilder;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-6 02:25 * @Date 2024-3-6 02:25
*/ */
@Data @Data

View File

@ -11,8 +11,8 @@ import lombok.NoArgsConstructor;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-1 11:02 * @Date 2024-3-1 11:02
*/ */
@Data @Data

View File

@ -12,8 +12,8 @@ import lombok.NoArgsConstructor;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-25 10:46 * @Date 2024-3-25 10:46
*/ */
@Data @Data

View File

@ -13,8 +13,8 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-3-4 04:08 * @Date 2024-3-4 04:08
*/ */
@Data @Data

View File

@ -6,9 +6,11 @@ import com.muyu.common.swagger.annotation.EnableCustomSwagger2;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.ArrayList;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2024-2-26 04:07 * @Date 2024-2-26 04:07
*/ */
@EnableCustomConfig @EnableCustomConfig
@ -17,6 +19,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class MuYuProductApplication { public class MuYuProductApplication {
public static void main (String[] args) { public static void main (String[] args) {
ArrayList< Long > objects = new ArrayList<> ( );
objects.add ( 1L );
SpringApplication.run(MuYuProductApplication.class, args); SpringApplication.run(MuYuProductApplication.class, args);
} }

View File

@ -7,8 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
* @author DongZl *
* @description:
* @Date 2024-3-27 03:37 * @Date 2024-3-27 03:37
*/ */
@Service @Service
@ -26,4 +25,6 @@ public class ProjectInfoDataImpl implements ProjectInfoData {
public ProjectInfo getData (Long key) { public ProjectInfo getData (Long key) {
return projectInfoService.getById(key); return projectInfoService.getById(key);
} }
} }

View File

@ -0,0 +1,51 @@
package com.muyu.product.cache.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.muyu.product.cache.datasource.ProjectSkuData;
import com.muyu.product.domain.ProjectSkuInfo;
import com.muyu.product.service.ProjectSkuInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* SKU
* @author YunFei.Du
* @date 14:20 2024/4/1
*/
@Service
public class ProjectSkuDataImpl implements ProjectSkuData {
@Autowired
private ProjectSkuInfoService projectSkuInfoService;
@Override
public Map< String, ProjectSkuInfo > getData(Long projectId) {
LambdaQueryWrapper< ProjectSkuInfo > queryWrapper = new LambdaQueryWrapper< ProjectSkuInfo > ( )
.eq ( ProjectSkuInfo::getProjectId, projectId );
List< ProjectSkuInfo > projectSkuInfoList = projectSkuInfoService.list ( queryWrapper );
Map< String, ProjectSkuInfo > map = new HashMap<> ( );
projectSkuInfoList.stream ().map (
projectSkuInfo -> {
map.put ( projectSkuInfo.getSku (),projectSkuInfo );
return projectSkuInfo;
}
).collect( Collectors.toList());
return map;
}
@Override
public ProjectSkuInfo getData(Long projectId, String projectSku) {
LambdaQueryWrapper< ProjectSkuInfo > queryWrapper = new LambdaQueryWrapper< ProjectSkuInfo > ( )
.eq ( ProjectSkuInfo::getProjectId, projectId )
.eq ( ProjectSkuInfo::getSku, projectSku );
return projectSkuInfoService.getOne ( queryWrapper );
}
}

View File

@ -0,0 +1,31 @@
package com.muyu.product.cache.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.muyu.product.cache.datasource.ProjectSkuStockData;
import com.muyu.product.cache.key.SkuStockKey;
import com.muyu.product.domain.ProjectSkuInfo;
import com.muyu.product.service.ProjectSkuInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author DongZl
* @description: sku
* @Date 2024-4-2 10:53
*/
@Service
public class ProjectSkuStockDataImpl implements ProjectSkuStockData {
@Autowired
private ProjectSkuInfoService projectSkuInfoService;
@Override
public Long getData (SkuStockKey key) {
LambdaQueryWrapper<ProjectSkuInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ProjectSkuInfo::getProjectId, key.getProjectId());
queryWrapper.eq(ProjectSkuInfo::getSku, key.getSku());
ProjectSkuInfo projectSkuInfo = projectSkuInfoService.getOne(queryWrapper);
return projectSkuInfo.getStock();
}
}

View File

@ -55,7 +55,6 @@ public class ProjectInfoController extends BaseController {
List<ProjectInfo> list = projectInfoService.list(ProjectInfo.queryBuild(projectInfoQueryReq)); List<ProjectInfo> list = projectInfoService.list(ProjectInfo.queryBuild(projectInfoQueryReq));
return getDataTable(list); return getDataTable(list);
} }
/** /**
* *
*/ */

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.muyu</groupId>
<artifactId>muyu-shop-cart</artifactId>
<version>3.6.3</version>
</parent>
<artifactId>muyu-shop-cart-cache</artifactId>
<dependencies>
<!-- 商品服务 公共 依赖 -->
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-shop-cart-common</artifactId>
</dependency>
<!-- 缓存基准依赖 -->
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-common-cache</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,107 @@
package com.muyu.shop.cart.cache;
import com.muyu.common.cache.abs.HashCacheAbs;
import com.muyu.common.core.domain.Result;
import com.muyu.common.core.exception.ServiceException;
import com.muyu.common.core.text.Convert;
import com.muyu.shop.cart.cache.key.CartHashKey;
import com.muyu.shop.cart.cache.sourcedata.CartData;
import com.muyu.shop.cart.domain.CartInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author DongZl
* @description:
* @Date 2024-4-2 11:23
*/
@Component
public class CartCache extends HashCacheAbs<Long, CartHashKey, CartInfo> {
@Autowired
private CartData cartData;
/**
* key
* @return key
*/
@Override
public String keyPre () {
return "cart:info:";
}
/**
*
* @param redisKey
* @return ID
*/
@Override
public Long decode (String redisKey) {
return Convert.toLong(redisKey.replace(keyPre(), ""));
}
/**
*
*
* @param hashKey ID
*
* @return
*/
@Override
public String encodeHashKey (CartHashKey hashKey) {
return hashKey.getProjectId()+":"+hashKey.getProjectSku();
}
/**
*
* @param redisHashKey
* @return ID
*/
@Override
public CartHashKey decodeHashKey (String redisHashKey) {
String[] split = redisHashKey.split(":");
return CartHashKey.builder()
.projectId(Convert.toLong(split[0]))
.projectSku(split[1])
.build();
}
/**
* hash
* @param key
* @return
*/
@Override
public Map<CartHashKey, CartInfo> getData (Long key) {
return cartData.getData(key);
}
/**
* hashhash
*
* @param key
* @param hashKey hash
*
* @return hash
*/
@Override
public CartInfo getData (Long key, CartHashKey hashKey) {
return cartData.getData(key, hashKey);
}
/**
*
*/
@Override
public Map<CartHashKey, CartInfo> defaultValue () {
throw new ServiceException("购物车无数据", Result.SUCCESS);
}
@Override
public CartInfo defaultHashValue () {
throw new ServiceException("购物车无数据", Result.SUCCESS);
}
}

View File

@ -0,0 +1,28 @@
package com.muyu.shop.cart.cache.key;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author DongZl
* @description: HashKey
* @Date 2024-4-2 11:25
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CartHashKey {
/**
* ID
*/
private Long projectId;
/**
* SKU
*/
private String projectSku;
}

View File

@ -0,0 +1,31 @@
package com.muyu.shop.cart.cache.sourcedata;
import com.muyu.shop.cart.cache.key.CartHashKey;
import com.muyu.shop.cart.domain.CartInfo;
import java.util.Map;
/**
* @author DongZl
* @description:
* @Date 2024-4-2 11:49
*/
public interface CartData {
/**
* hash
* @param key
* @return
*/
public Map<CartHashKey, CartInfo> getData (Long key);
/**
* hashhash
*
* @param key
* @param hashKey hash
*
* @return hash
*/
public CartInfo getData (Long key, CartHashKey hashKey);
}

View File

@ -15,6 +15,8 @@ import com.muyu.shop.cart.domain.req.CartInfoSaveReq;
import com.muyu.shop.cart.domain.req.CartInfoEditReq; import com.muyu.shop.cart.domain.req.CartInfoEditReq;
import com.muyu.common.core.web.domain.BaseEntity; import com.muyu.common.core.web.domain.BaseEntity;
import java.util.function.Supplier;
/** /**
* cart_info * cart_info
* *

View File

@ -23,6 +23,7 @@ public class CartInfoQueryReq extends BaseEntity {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 商品 */ /** 商品 */
@ApiModelProperty(name = "商品", value = "商品") @ApiModelProperty(name = "商品", value = "商品")
private Long projectId; private Long projectId;

View File

@ -87,6 +87,26 @@
<version>${muyu.version}</version> <version>${muyu.version}</version>
</dependency> </dependency>
<!-- 购物车模块 缓存 依赖 -->
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-shop-cart-cache</artifactId>
<version>${muyu.version}</version>
</dependency>
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-product-cache</artifactId>
</dependency>
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-product-server</artifactId>
<version>3.6.3</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,10 @@
package com.muyu.shop.cart.cache.impl;
/**
* @ClassName CartInfoDataImpl
* @Description
* @Author YunFei.Du
* @Date 2024/3/31 10:35
*/
public interface CartInfoDataImpl {
}

View File

@ -0,0 +1,26 @@
package com.muyu.shop.cart.cache.impl;
import com.muyu.product.cache.datasource.ProjectInfoData;
import com.muyu.product.domain.ProjectInfo;
import org.springframework.stereotype.Service;
/**
*
* @Date 2024-3-27 03:37
*/
@Service
public class ProjectInfoDataImpl implements ProjectInfoData {
/**
*
* @param key ID
* @return
*/
@Override
public ProjectInfo getData (Long key) {
return new ProjectInfo ();
}
}

View File

@ -0,0 +1,34 @@
package com.muyu.shop.cart.cache.impl;
import com.muyu.product.cache.datasource.ProjectSkuData;
import com.muyu.product.domain.ProjectSkuInfo;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* SKU
* @author YunFei.Du
* @date 14:20 2024/4/1
*/
@Service
public class ProjectSkuDataImpl implements ProjectSkuData {
@Override
public Map< String, ProjectSkuInfo > getData(Long projectId) {
return new HashMap<> ();
}
@Override
public ProjectSkuInfo getData(Long projectId, String projectSku) {
return new ProjectSkuInfo ();
}
}

View File

@ -0,0 +1,23 @@
package com.muyu.shop.cart.cache.impl;
import com.muyu.product.cache.datasource.ProjectSkuStockData;
import com.muyu.product.cache.key.SkuStockKey;
import org.springframework.stereotype.Service;
/**
* @author DongZl
* @description: sku
* @Date 2024-4-2 10:53
*/
@Service
public class ProjectSkuStockDataImpl implements ProjectSkuStockData {
@Override
public Long getData (SkuStockKey key) {
return null;
}
}

View File

@ -51,6 +51,7 @@ public class CartInfoController extends BaseController {
return getDataTable(list); return getDataTable(list);
} }
/** /**
* *
*/ */
@ -60,7 +61,7 @@ public class CartInfoController extends BaseController {
@PostMapping("/export") @PostMapping("/export")
public void export(HttpServletResponse response, CartInfo cartInfo) { public void export(HttpServletResponse response, CartInfo cartInfo) {
List<CartInfo> list = cartInfoService.list(cartInfo); List<CartInfo> list = cartInfoService.list(cartInfo);
ExcelUtil<CartInfo> util = new ExcelUtil<CartInfo>(CartInfo.class); ExcelUtil<CartInfo> util = new ExcelUtil<>(CartInfo.class);
util.exportExcel(response, list, "购物车数据"); util.exportExcel(response, list, "购物车数据");
} }
@ -83,7 +84,7 @@ public class CartInfoController extends BaseController {
@PostMapping @PostMapping
@ApiOperation("新增购物车") @ApiOperation("新增购物车")
public Result<String> add(@RequestBody CartInfoSaveReq cartInfoSaveReq) { public Result<String> add(@RequestBody CartInfoSaveReq cartInfoSaveReq) {
return toAjax(cartInfoService.save(CartInfo.saveBuild(cartInfoSaveReq))); return toAjax(cartInfoService.add(CartInfo.saveBuild (cartInfoSaveReq)));
} }
/** /**

View File

@ -3,6 +3,7 @@ package com.muyu.shop.cart.service;
import java.util.List; import java.util.List;
import com.muyu.shop.cart.domain.CartInfo; import com.muyu.shop.cart.domain.CartInfo;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.muyu.shop.cart.domain.req.CartInfoSaveReq;
/** /**
* Service * Service
@ -11,6 +12,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
* @date 2024-03-29 * @date 2024-03-29
*/ */
public interface CartInfoService extends IService<CartInfo> { public interface CartInfoService extends IService<CartInfo> {
/** /**
* *
* *
@ -19,4 +22,14 @@ public interface CartInfoService extends IService<CartInfo> {
*/ */
public List<CartInfo> list(CartInfo cartInfo); public List<CartInfo> list(CartInfo cartInfo);
boolean insertCartInfo (CartInfoSaveReq cartInfoSaveReq);
/**
*
* @param cartInfo
* @return
*/
boolean add(CartInfo cartInfo);
} }

View File

@ -1,9 +1,23 @@
package com.muyu.shop.cart.service.impl; package com.muyu.shop.cart.service.impl;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.muyu.common.core.constant.UserConstants;
import com.muyu.common.core.exception.ServiceException;
import com.muyu.common.core.text.Convert;
import com.muyu.common.core.utils.ObjUtils; import com.muyu.common.core.utils.ObjUtils;
import com.muyu.common.security.utils.SecurityUtils;
import com.muyu.product.cache.ProjectSkuStockCache;
import com.muyu.product.cache.key.SkuStockKey;
import com.muyu.shop.cart.cache.CartCache;
import com.muyu.shop.cart.cache.key.CartHashKey;
import com.muyu.shop.cart.domain.req.CartInfoSaveReq;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.security.SecurityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.muyu.shop.cart.mapper.CartInfoMapper; import com.muyu.shop.cart.mapper.CartInfoMapper;
import com.muyu.shop.cart.domain.CartInfo; import com.muyu.shop.cart.domain.CartInfo;
@ -21,6 +35,59 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@Service @Service
public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartInfoService { public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartInfoService {
@Autowired
private CartCache cartCache;
@Autowired
private ProjectSkuStockCache projectSkuStockCache;
@Override
public boolean add(CartInfo cartInfo) {
Long userId=SecurityUtils.getUserId ();
CartHashKey cartHashKey = CartHashKey.builder ( )
.projectId ( cartInfo.getProjectId ( ) )
.projectSku ( cartInfo.getProjectSku ( ) )
.build ( );
SkuStockKey skuStockKey = SkuStockKey.builder ( )
.projectId ( cartInfo.getProjectId () )
.sku ( cartInfo.getProjectSku () )
.build ();
// Long skuStock = projectSkuStockCache.get ( skuStockKey );
Long skuStock = Convert.toLong ( projectSkuStockCache.get ( skuStockKey ) ,-1L);
if (cartCache.hasKey(userId,cartHashKey)){
CartInfo cartInfoCache = cartCache.get ( userId, cartHashKey );
cartInfoCache.setNum ( cartInfoCache.getNum ()+cartInfo.getNum () );
if (skuStock>cartInfoCache.getNum ()){
throw new ServiceException ( "库存不足" );
}
LambdaUpdateWrapper< CartInfo > updateWrapper = new LambdaUpdateWrapper<> ( );
updateWrapper.set ( CartInfo::getNum,cartInfoCache.getNum () );
updateWrapper.eq ( CartInfo::getId,cartInfoCache.getId () );
this.update ( updateWrapper );
this.cartCache.put ( userId,cartHashKey,cartInfoCache );
}else {
if (skuStock>cartInfo.getNum ()){
throw new ServiceException ( "库存不足" );
}
cartInfo.setUserId ( userId );
cartInfo.setIsSelected ( "Y" );
cartInfo.setCreateBy ( SecurityUtils.getUsername () );
cartInfo.setCreateTime ( new Date () );
this.save ( cartInfo );
this.cartCache.put ( userId,cartHashKey,cartInfo );
}
return true;
}
/** /**
* *
* *
@ -53,9 +120,27 @@ public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo>
} }
return list(queryWrapper); return list(queryWrapper);
} }
@Override
public boolean insertCartInfo(CartInfoSaveReq cartInfoSaveReq) {
cartInfoSaveReq.setUserId ( SecurityUtils.getUserId () );
LambdaQueryWrapper< CartInfo > queryWrapper = new LambdaQueryWrapper<> ( ) {{
eq ( CartInfo::getProjectSku, cartInfoSaveReq.getProjectSku ( ) );
}};
CartInfo one = this.getOne ( queryWrapper );
if (one==null){
this.save ( CartInfo.saveBuild(cartInfoSaveReq) );
}
LambdaUpdateWrapper< CartInfo > updateWrapper = new LambdaUpdateWrapper< CartInfo > ( )
.eq ( CartInfo::getProjectSku, cartInfoSaveReq.getProjectSku ( ) )
.setSql ( "num = num +" + cartInfoSaveReq.getNum ( ) );
boolean update = this.update ( updateWrapper );
return update;
}
} }

View File

@ -0,0 +1,70 @@
package com.muyu.shop.cart.service.sourcedata;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.muyu.shop.cart.cache.key.CartHashKey;
import com.muyu.shop.cart.cache.sourcedata.CartData;
import com.muyu.shop.cart.domain.CartInfo;
import com.muyu.shop.cart.service.CartInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
* @author YunFei.Du
* @date 14:43 2024/4/2
*/
@Service
public class CartDataImpl implements CartData {
@Autowired
private CartInfoService cartInfoService;
/**
* hash
*
* @param key
*
* @return
*/
@Override
public Map< CartHashKey, CartInfo > getData (Long key) {
LambdaQueryWrapper<CartInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CartInfo::getUserId, key);
List<CartInfo> cartInfoList = cartInfoService.list(queryWrapper);
// Map<CartHashKey, CartInfo> map=new HashMap<> ( );
// if (cartInfoList!=null&&cartInfoList.size ()>0){
// for (CartInfo cartInfo : cartInfoList) {
// map.put ( new CartHashKey ( cartInfo.getProjectId (),cartInfo.getProjectSku () ),cartInfo );
// }
// }
// return map;
return cartInfoList.stream ()
.collect( Collectors.toMap ( cartInfo ->CartHashKey.builder ( ).projectId ( cartInfo.getProjectId () ).projectSku ( cartInfo.getProjectSku () ).build (), cartInfo -> cartInfo ));
}
/**
* hashhash
*
* @param key
* @param hashKey hash
*
* @return hash
*/
@Override
public CartInfo getData (Long key, CartHashKey hashKey) {
CartInfo cartInfo = cartInfoService.getOne(new LambdaQueryWrapper<CartInfo>()
.eq(CartInfo::getUserId, key)
.eq(CartInfo::getProjectId, hashKey.getProjectId())
.eq(CartInfo::getProjectSku, hashKey.getProjectSku()));
return cartInfo;
}
}

View File

@ -4,6 +4,8 @@ server:
# Spring # Spring
spring: spring:
main:
allow-circular-references: true
application: application:
# 应用名称 # 应用名称
name: muyu-shop-cart name: muyu-shop-cart

View File

@ -15,6 +15,7 @@
<module>muyu-shop-cart-common</module> <module>muyu-shop-cart-common</module>
<module>muyu-shop-cart-remote</module> <module>muyu-shop-cart-remote</module>
<module>muyu-shop-cart-server</module> <module>muyu-shop-cart-server</module>
<module>muyu-shop-cart-cache</module>
</modules> </modules>
<properties> <properties>

View File

@ -11,8 +11,8 @@ import lombok.experimental.SuperBuilder;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-6-19 02:50 * @Date 2023-6-19 02:50
*/ */
@Data @Data

View File

@ -10,8 +10,8 @@ import lombok.experimental.SuperBuilder;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-6-19 02:52 * @Date 2023-6-19 02:52
*/ */
@Data @Data

View File

@ -7,8 +7,8 @@ import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-6-19 02:05 * @Date 2023-6-19 02:05
*/ */
@Data @Data

View File

@ -9,8 +9,8 @@ import lombok.experimental.SuperBuilder;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-6-19 02:40 * @Date 2023-6-19 02:40
*/ */
@Data @Data

View File

@ -10,8 +10,8 @@ import lombok.experimental.SuperBuilder;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-6-19 02:45 * @Date 2023-6-19 02:45
*/ */
@Data @Data

View File

@ -9,8 +9,8 @@ import lombok.experimental.SuperBuilder;
import java.util.Set; import java.util.Set;
/** /**
* @author DongZl * dyf
* @description: *
* @Date 2023-6-19 02:42 * @Date 2023-6-19 02:42
*/ */
@Data @Data

View File

@ -4,8 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.muyu.system.domain.SysConfig; import com.muyu.system.domain.SysConfig;
/** /**
* @author DongZl * dyf
* @description: mybatis * mybatis
* @Date 2023-11-13 10:05 * @Date 2023-11-13 10:05
*/ */
public interface SysConfigMapper extends BaseMapper<SysConfig> { public interface SysConfigMapper extends BaseMapper<SysConfig> {

View File

@ -6,8 +6,8 @@ import com.muyu.system.domain.SysConfig;
import java.util.List; import java.util.List;
/** /**
* @author DongZl * dyf
* @description: plus * plus
* @Date 2023-11-13 10:06 * @Date 2023-11-13 10:06
*/ */
public interface SysConfigService extends IService<SysConfig> { public interface SysConfigService extends IService<SysConfig> {

View File

@ -18,8 +18,8 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* @author DongZl * dyf
* @description: plus * plus
* @Date 2023-11-13 10:06 * @Date 2023-11-13 10:06
*/ */
@Service @Service