Commit bdb8936e by Lizh

调整integrate目录结构,删除无用代码

parent 1c5f75aa
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
/logs/ /logs/
.trae/ .trae/
.vscode/ .vscode/
.claude/
CLAUDE.md CLAUDE.md
docs docs
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelPageVO;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelVO;
import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity;
import java.util.List;
/**
* @author Lizh
* @version 0.01
* @description: 接口
* @date 2026-05-29 10:43:29
*/
public interface CustomProductWarehouseRelService {
/**
* 列表查询接口
*
* @param customProductWarehouseRelVO 条件model
* @return list集合
*/
List<CustomProductWarehouseRelVO> list(CustomProductWarehouseRelVO customProductWarehouseRelVO);
/**
* 根据条件查询分页列表接口
*
* @param customProductWarehouseRelPageVO 分页入参model
* @return 分页对象
*/
IPage<CustomProductWarehouseRelVO> pageList(CustomProductWarehouseRelPageVO customProductWarehouseRelPageVO);
/**
* 根据id查询详情
*
* @param id 主键
* @return 实体model
*/
CustomProductWarehouseRelVO info(Integer id);
/**
* 保存对象
*
* @param customProductWarehouseRelVO 保存对象
*/
void save(CustomProductWarehouseRelVO customProductWarehouseRelVO);
/**
* 根据id修改对象
*
* @param customProductWarehouseRelVO 修改对象
*/
void updateById(CustomProductWarehouseRelVO customProductWarehouseRelVO);
/**
* 根据主键ID进行删除
*
* @param id 主键
*/
void deleteById(Integer id);
/**
* 根据仓库ID获取关联列表
*/
List<CustomProductWarehouseRelEntity> getListByWareHouseId(Long warehouseId);
/**
* 根据产品ID获取关联列表
*/
List<CustomProductWarehouseRelEntity> getListByProductId(Long productId);
/**
* 根据产品ID获取仓库ID列表
*/
List<Long> getWarehouseIdsByProductId(Long productId);
/**
* 批量保存产品与仓库的关联
* 逻辑:先删除该产品现有的所有关联,再批量插入新的
*/
void saveBatch(Integer productId, List<Long> warehouseIds);
/**
* 根据产品ID删除关联
* 注意:原 TS 代码支持手动传入 transaction,Java 中通常使用 @Transactional 注解管理事务
*/
void deleteByProductId(Integer productId);
}
package com.jomalls.custom.app.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelPageVO;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelVO;
import com.jomalls.custom.app.service.CustomProductWarehouseRelService;
import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity;
import com.jomalls.custom.domain.service.CustomProductWarehouseRelDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Lizh
* @version 0.01
* @description: 接口实现
* @date 2026-05-29 10:43:29
*/
@Slf4j
@Service
public class CustomProductWarehouseRelServiceImpl implements CustomProductWarehouseRelService {
private final CustomProductWarehouseRelDomainService customProductWarehouseRelDomainService;
@Autowired
public CustomProductWarehouseRelServiceImpl(CustomProductWarehouseRelDomainService customProductWarehouseRelDomainService) {
this.customProductWarehouseRelDomainService = customProductWarehouseRelDomainService;
}
@Override
public List<CustomProductWarehouseRelVO> list(CustomProductWarehouseRelVO customProductWarehouseRelVO) {
QueryWrapper<CustomProductWarehouseRelEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参
List<CustomProductWarehouseRelEntity> list = customProductWarehouseRelDomainService.list(queryWrapper);
return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductWarehouseRelVO.class)).collect(Collectors.toList());
}
@Override
public IPage<CustomProductWarehouseRelVO> pageList(CustomProductWarehouseRelPageVO customProductWarehouseRelPageVO) {
CustomAsserts.nonNull(customProductWarehouseRelPageVO, "分页查询参数不能为空");
QueryWrapper<CustomProductWarehouseRelEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参
IPage<CustomProductWarehouseRelEntity> page = customProductWarehouseRelDomainService.selectPage(queryWrapper, customProductWarehouseRelPageVO);
return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductWarehouseRelVO.class));
}
@Override
public CustomProductWarehouseRelVO info(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空");
CustomProductWarehouseRelEntity customProductWarehouseRel = customProductWarehouseRelDomainService.getById(id);
return BeanMapper.mapper().convert(customProductWarehouseRel, CustomProductWarehouseRelVO.class);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void save(CustomProductWarehouseRelVO customProductWarehouseRelVO) {
CustomAsserts.nonNull(customProductWarehouseRelVO, "实体对象不能为空");
CustomProductWarehouseRelEntity customProductWarehouseRelEntity = BeanMapper.mapper().convert(customProductWarehouseRelVO, CustomProductWarehouseRelEntity.class);
try {
customProductWarehouseRelDomainService.save(customProductWarehouseRelEntity);
} catch (DuplicateKeyException e) {
log.info("[ CustomProductWarehouseRelServiceImpl save ] 实体对象唯一约束重复,请调整后再试!", e);
throw new ServiceException("实体对象唯一约束重复,请调整后再试!");
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateById(CustomProductWarehouseRelVO customProductWarehouseRelVO) {
CustomAsserts.nonNull(customProductWarehouseRelVO, "实体对象不能为空");
CustomProductWarehouseRelEntity customProductWarehouseRel = BeanMapper.mapper().convert(customProductWarehouseRelVO, CustomProductWarehouseRelEntity.class);
try {
customProductWarehouseRelDomainService.updateById(customProductWarehouseRel);
} catch (DuplicateKeyException e) {
log.info("[ CustomProductWarehouseRelServiceImpl updateById ] 实体对象唯一约束重复,请调整后再试!", e);
throw new ServiceException("实体对象唯一约束重复,请调整后再试!");
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteById(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空");
customProductWarehouseRelDomainService.removeById(id);
}
/**
* 根据仓库ID获取关联列表
*/
@Override
public List<CustomProductWarehouseRelEntity> getListByWareHouseId(Long warehouseId) {
return customProductWarehouseRelDomainService.list(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getWarehouseId, warehouseId));
}
/**
* 根据产品ID获取关联列表
*/
@Override
public List<CustomProductWarehouseRelEntity> getListByProductId(Long productId) {
return customProductWarehouseRelDomainService.list(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId));
}
/**
* 根据产品ID获取仓库ID列表
*/
@Override
public List<Long> getWarehouseIdsByProductId(Long productId) {
List<CustomProductWarehouseRelEntity> rels = customProductWarehouseRelDomainService.list(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId)
.select(CustomProductWarehouseRelEntity::getWarehouseId)); // 只查询需要的字段以提高性能
if (CollectionUtils.isEmpty(rels)) {
return null;
}
return rels.stream()
.map(CustomProductWarehouseRelEntity::getWarehouseId)
.collect(Collectors.toList());
}
/**
* 根据产品ID删除关联
* 注意:原 TS 代码支持手动传入 transaction,Java 中通常使用 @Transactional 注解管理事务
*/
@Override
public void deleteByProductId(Integer productId) {
customProductWarehouseRelDomainService.remove(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId));
}
/**
* 批量保存产品与仓库的关联
* 逻辑:先删除该产品现有的所有关联,再批量插入新的
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveBatch(Integer productId, List<Long> warehouseIds) {
this.deleteByProductId(productId);
if (CollectionUtils.isEmpty(warehouseIds)) {
return;
}
List<CustomProductWarehouseRelEntity> relList = new ArrayList<>();
for (Long warehouseId : warehouseIds) {
CustomProductWarehouseRelEntity rel = new CustomProductWarehouseRelEntity();
rel.setProductId(productId);
rel.setWarehouseId(warehouseId);
relList.add(rel);
}
customProductWarehouseRelDomainService.saveBatch(relList);
}
}
...@@ -42,15 +42,14 @@ public class DiyUserServiceImpl implements DiyUserService { ...@@ -42,15 +42,14 @@ public class DiyUserServiceImpl implements DiyUserService {
if (StringUtils.isBlank(namespace)) { if (StringUtils.isBlank(namespace)) {
return null; return null;
} }
DbDiyUserEntity user = dbDiyUserDomainService.getByNamespace(namespace); return dbDiyUserDomainService.getByNamespace(namespace);
if (user == null) {
throw new ServiceException("用户不存在, namespace=" + namespace);
}
return user;
} }
@Override @Override
public DbDiyUserEntity getByUserMark(String userMark) { public DbDiyUserEntity getByUserMark(String userMark) {
if (StringUtils.isBlank(userMark)) {
return null;
}
return dbDiyUserDomainService.getByUserMark(userMark); return dbDiyUserDomainService.getByUserMark(userMark);
} }
......
...@@ -24,7 +24,7 @@ import java.util.List; ...@@ -24,7 +24,7 @@ import java.util.List;
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public class PageAdaptter<T> implements Serializable { public class PageAdapter<T> implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
...@@ -51,8 +51,8 @@ public class PageAdaptter<T> implements Serializable { ...@@ -51,8 +51,8 @@ public class PageAdaptter<T> implements Serializable {
* @param <T> 数据类型 * @param <T> 数据类型
* @return 老服务格式的分页结果 * @return 老服务格式的分页结果
*/ */
public static <T> PageAdaptter<T> from(IPage<T> page) { public static <T> PageAdapter<T> from(IPage<T> page) {
return new PageAdaptter<>( return new PageAdapter<>(
page.getRecords(), page.getRecords(),
page.getCurrent(), page.getCurrent(),
page.getTotal(), page.getTotal(),
......
...@@ -8,6 +8,7 @@ import lombok.Data; ...@@ -8,6 +8,7 @@ import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
/** /**
...@@ -50,11 +51,11 @@ public class CraftCenterEntity implements Serializable { ...@@ -50,11 +51,11 @@ public class CraftCenterEntity implements Serializable {
/** 工艺成本 */ /** 工艺成本 */
@TableField("craft_cost") @TableField("craft_cost")
private String craftCost; private BigDecimal craftCost;
/** 其他面的价格 */ /** 其他面的价格 */
@TableField("other_side_cost") @TableField("other_side_cost")
private String otherSideCost; private BigDecimal otherSideCost;
/** 是否启用(1启用,0未启用) */ /** 是否启用(1启用,0未启用) */
@TableField("craft_enable") @TableField("craft_enable")
......
...@@ -33,44 +33,6 @@ ...@@ -33,44 +33,6 @@
create_by, create_time, update_by, update_time, remark create_by, create_time, update_by, update_time, remark
</sql> </sql>
<!-- 根据用户名查询用户 -->
<select id="selectByUsername" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE user_name = #{userName}
AND status = '0'
AND del_flag = '0'
</select>
<!-- 根据邮箱查询用户 -->
<select id="selectByEmail" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE email = #{email}
AND status = '0'
AND del_flag = '0'
</select>
<!-- 分页查询用户列表 -->
<select id="selectUserPage" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE del_flag = '0'
<if test="status != null">
AND status = #{status}
</if>
ORDER BY create_time DESC
</select>
<!-- 查询所有启用状态的用户 -->
<select id="selectAllActiveUsers" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE status = '0'
AND del_flag = '0'
ORDER BY create_time DESC
</select>
<!-- 批量插入 --> <!-- 批量插入 -->
<insert id="insertBatchSomeColumn"> <insert id="insertBatchSomeColumn">
INSERT INTO sys_user (user_name, nick_name, user_type, INSERT INTO sys_user (user_name, nick_name, user_type,
......
...@@ -44,7 +44,7 @@ public class WebClientConfig { ...@@ -44,7 +44,7 @@ public class WebClientConfig {
public ConnectionProvider connectionProvider() { public ConnectionProvider connectionProvider() {
return ConnectionProvider.builder("custom-server-http-pool") return ConnectionProvider.builder("custom-server-http-pool")
.maxConnections(poolMaxConnections) .maxConnections(poolMaxConnections)
.pendingAcquireMaxCount(-1) .pendingAcquireMaxCount(100)
.pendingAcquireTimeout(Duration.ofMillis(poolAcquireTimeout)) .pendingAcquireTimeout(Duration.ofMillis(poolAcquireTimeout))
.maxIdleTime(Duration.ofSeconds(60)) .maxIdleTime(Duration.ofSeconds(60))
.maxLifeTime(Duration.ofMinutes(5)) .maxLifeTime(Duration.ofMinutes(5))
......
...@@ -27,44 +27,10 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -27,44 +27,10 @@ import java.util.concurrent.atomic.AtomicReference;
* 调用 admin API 获取商品分类数据。 * 调用 admin API 获取商品分类数据。
* 对齐 TS 项目 {@code baseCategoryInfoService}。 * 对齐 TS 项目 {@code baseCategoryInfoService}。
* *
* @author zhengcunwen
* @author Lizh (Java 迁移) * @author Lizh (Java 迁移)
* @since 2019-09-02 * @since 2019-09-02
*/ */
@Slf4j public interface SaasAdminService {
@Service
@RequiredArgsConstructor
public class SaasAdminService {
private static final String GET_TREE_URL = "/api/manage/rest/baseCategoryInfo/tree_list";
private static final String GET_BY_IDS_URL = "/api/manage/rest/baseCategoryInfo/getDataByIds";
private static final String GET_BY_ID_URL = "/api/manage/rest/baseCategoryInfo/get";
private static final String GET_ALL_LIST_URL = "/api/manage/rest/baseCategoryInfo/all_list";
private static final String GET_PROPERTY_BY_IDS_URL = "/api/manage/rest/baseProperty/getByIds";
private static final int DEFAULT_MAP_SIZE = 2;
/** 分类列表缓存 TTL(毫秒):5 分钟 */
private static final long CACHE_TTL_MS = 5 * 60 * 1000;
private final RemoteApiClient remoteApiClient;
/** 分类列表本地缓存 */
private final AtomicReference<CacheEntry<List<CategoryInfoModel>>> categoryCache = new AtomicReference<>();
@Value("${server.admin.base-url:https://admin.jomalls.com}")
private String adminBaseUrl;
private Map<String, String> getHeader() {
Map<String, String> headers = new HashMap<>(DEFAULT_MAP_SIZE);
headers.put("Content-Type", "application/json");
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return headers;
}
headers.put("jwt-token", loginUser.getToken());
return headers;
}
/** /**
* 根据属性 ID 列表批量查询属性(含属性值列表) * 根据属性 ID 列表批量查询属性(含属性值列表)
...@@ -74,115 +40,7 @@ public class SaasAdminService { ...@@ -74,115 +40,7 @@ public class SaasAdminService {
* @param ids 属性 ID,逗号分隔(如 "1,2,3") * @param ids 属性 ID,逗号分隔(如 "1,2,3")
* @return 属性列表,包含 valueList 和 skuProperty 等字段 * @return 属性列表,包含 valueList 和 skuProperty 等字段
*/ */
public List<PropertyModel> getPropertyByIds(String ids) { List<PropertyModel> getPropertyByIds(String ids);
if (!StringUtils.hasText(ids)) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_PROPERTY_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<SaasAdminApiResponseModel<List<PropertyModel>>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
log.debug("[ SaasAdminService ] getByIds 成功, ids={}, 返回: {}", ids, response.toString());
SaasAdminApiResponseModel<List<PropertyModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
return responseBody.getData();
}
}
log.warn("[ SaasAdminService ] getByIds 返回空, ids={}", ids);
return Collections.emptyList();
} catch (Exception e) {
log.error("[ SaasAdminService ] getByIds 调用失败, ids={}", ids, e);
return Collections.emptyList();
}
}
/**
* 获取分类树
* <p>
* 对齐 TS {@code getTree()}
*/
public List<CategoryInfoModel> getTree() {
try {
ResponseEntity<List<CategoryInfoModel>> response = remoteApiClient.get(
adminBaseUrl + GET_TREE_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getTree 调用失败", e);
}
return Collections.emptyList();
}
/**
* 根据 ID 列表批量查询
* <p>
* 对齐 TS {@code getByIds(ids)}
*/
public List<CategoryInfoModel> getByIds(String ids) {
if (ids == null || ids.isEmpty()) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<List<CategoryInfoModel>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getByIds 调用失败, ids={}", ids, e);
}
return Collections.emptyList();
}
/**
* 获取带有风格属性的树形结构
* <p>
* 对齐 TS {@code treeList()}
*/
public List<CategoryInfoModel> treeList() {
try {
ResponseEntity<List<CategoryInfoModel>> response = remoteApiClient.get(
adminBaseUrl + GET_TREE_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] treeList 调用失败", e);
}
return Collections.emptyList();
}
/**
* 根据 ID 查询单个分类
* <p>
* 对齐 TS {@code getById(id)}
*/
public CategoryInfoModel getById(Integer id) {
if (id == null) {
return null;
}
try {
String url = adminBaseUrl + GET_BY_ID_URL + "?id=" + id;
ResponseEntity<CategoryInfoModel> response = remoteApiClient.get(
url, CategoryInfoModel.class, null);
if (response != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getById 调用失败, id={}", id, e);
}
return null;
}
/** /**
* 查询所有分类(带本地缓存,TTL 5 分钟) * 查询所有分类(带本地缓存,TTL 5 分钟)
...@@ -190,49 +48,5 @@ public class SaasAdminService { ...@@ -190,49 +48,5 @@ public class SaasAdminService {
* 对齐 TS {@code allList()}。分类数据不频繁变动, * 对齐 TS {@code allList()}。分类数据不频繁变动,
* 使用本地缓存避免每次分页查询都发起远程 HTTP 调用。 * 使用本地缓存避免每次分页查询都发起远程 HTTP 调用。
*/ */
public List<CategoryInfoModel> getAllList() { List<CategoryInfoModel> getAllList();
// 命中缓存且未过期 → 直接返回
CacheEntry<List<CategoryInfoModel>> entry = categoryCache.get();
if (entry != null && !entry.isExpired()) {
return entry.data;
}
// 未命中或已过期 → 远程调用刷新
try {
ResponseEntity<SaasAdminApiResponseModel<List<CategoryInfoModel>>> response = remoteApiClient.get(
adminBaseUrl + GET_ALL_LIST_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
SaasAdminApiResponseModel<List<CategoryInfoModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
List<CategoryInfoModel> data = responseBody.getData();
categoryCache.set(new CacheEntry<>(data));
return data;
}
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getAllList 调用失败", e);
}
// 远程调用失败但有旧缓存 → 降级返回旧缓存
if (entry != null) {
log.warn("[ SaasAdminService ] getAllList 远程失败,降级使用过期缓存");
return entry.data;
}
return Collections.emptyList();
}
/** 简单 TTL 缓存条目 */
private static class CacheEntry<T> {
final T data;
final long expireAt;
CacheEntry(T data) {
this.data = data;
this.expireAt = System.currentTimeMillis() + CACHE_TTL_MS;
}
boolean isExpired() {
return System.currentTimeMillis() > expireAt;
}
}
} }
package com.jomalls.custom.integrate.service.impl;
import com.jomalls.custom.enums.CodeEnum;
import com.jomalls.custom.integrate.client.RemoteApiClient;
import com.jomalls.custom.integrate.model.CategoryInfoModel;
import com.jomalls.custom.integrate.model.PropertyModel;
import com.jomalls.custom.integrate.model.SaasAdminApiResponseModel;
import com.jomalls.custom.integrate.service.SaasAdminService;
import com.jomalls.custom.security.LoginUser;
import com.jomalls.custom.security.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* 商品分类服务
* <p>
* 调用 admin API 获取商品分类数据。
* 对齐 TS 项目 {@code baseCategoryInfoService}。
*
* @author Lizh (Java 迁移)
* @since 2019-09-02
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SaasAdminServiceImpl implements SaasAdminService {
private static final String GET_ALL_LIST_URL = "/api/manage/rest/baseCategoryInfo/all_list";
private static final String GET_PROPERTY_BY_IDS_URL = "/api/manage/rest/baseProperty/getByIds";
private static final int DEFAULT_MAP_SIZE = 2;
/** 分类列表缓存 TTL(毫秒):5 分钟 */
private static final long CACHE_TTL_MS = 5 * 60 * 1000;
private final RemoteApiClient remoteApiClient;
/** 分类列表本地缓存 */
private final AtomicReference<CacheEntry<List<CategoryInfoModel>>> categoryCache = new AtomicReference<>();
@Value("${server.admin.base-url:https://admin.jomalls.com}")
private String adminBaseUrl;
private Map<String, String> getHeader() {
Map<String, String> headers = new HashMap<>(DEFAULT_MAP_SIZE);
headers.put("Content-Type", "application/json");
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return headers;
}
headers.put("jwt-token", loginUser.getToken());
return headers;
}
/**
* 根据属性 ID 列表批量查询属性(含属性值列表)
* <p>
* 对齐 TS {@code getByIds(ids)} 方法。
*
* @param ids 属性 ID,逗号分隔(如 "1,2,3")
* @return 属性列表,包含 valueList 和 skuProperty 等字段
*/
public List<PropertyModel> getPropertyByIds(String ids) {
if (!StringUtils.hasText(ids)) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_PROPERTY_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<SaasAdminApiResponseModel<List<PropertyModel>>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
log.debug("[ SaasAdminService ] getByIds 成功, ids={}, 返回: {}", ids, response.toString());
SaasAdminApiResponseModel<List<PropertyModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
return responseBody.getData();
}
}
log.warn("[ SaasAdminService ] getByIds 返回空, ids={}", ids);
return Collections.emptyList();
} catch (Exception e) {
log.error("[ SaasAdminService ] getByIds 调用失败, ids={}", ids, e);
return Collections.emptyList();
}
}
/**
* 查询所有分类(带本地缓存,TTL 5 分钟)
* <p>
* 对齐 TS {@code allList()}。分类数据不频繁变动,
* 使用本地缓存避免每次分页查询都发起远程 HTTP 调用。
*/
public List<CategoryInfoModel> getAllList() {
// 命中缓存且未过期 → 直接返回
CacheEntry<List<CategoryInfoModel>> entry = categoryCache.get();
if (entry != null && !entry.isExpired()) {
return entry.data;
}
// 未命中或已过期 → 远程调用刷新
try {
ResponseEntity<SaasAdminApiResponseModel<List<CategoryInfoModel>>> response = remoteApiClient.get(
adminBaseUrl + GET_ALL_LIST_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
SaasAdminApiResponseModel<List<CategoryInfoModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
List<CategoryInfoModel> data = responseBody.getData();
categoryCache.set(new CacheEntry<>(data));
return data;
}
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getAllList 调用失败", e);
}
// 远程调用失败但有旧缓存 → 降级返回旧缓存
if (entry != null) {
log.warn("[ SaasAdminService ] getAllList 远程失败,降级使用过期缓存");
return entry.data;
}
return Collections.emptyList();
}
/** 简单 TTL 缓存条目 */
private static class CacheEntry<T> {
final T data;
final long expireAt;
CacheEntry(T data) {
this.data = data;
this.expireAt = System.currentTimeMillis() + CACHE_TTL_MS;
}
boolean isExpired() {
return System.currentTimeMillis() > expireAt;
}
}
}
...@@ -3,7 +3,7 @@ package com.jomalls.custom.config; ...@@ -3,7 +3,7 @@ package com.jomalls.custom.config;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.jomalls.custom.enums.CodeEnum; import com.jomalls.custom.enums.CodeEnum;
import com.jomalls.custom.page.PageAdaptter; import com.jomalls.custom.page.PageAdapter;
import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NonNull;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
...@@ -63,7 +63,7 @@ public class RestResponseBodyConfig implements ResponseBodyAdvice<Object> { ...@@ -63,7 +63,7 @@ public class RestResponseBodyConfig implements ResponseBodyAdvice<Object> {
} else { } else {
// 分页结果转为老服务(TS)格式:records→list, current→curPage, total→totalRow, pages→totalPage // 分页结果转为老服务(TS)格式:records→list, current→curPage, total→totalRow, pages→totalPage
if (body instanceof IPage<?> page) { if (body instanceof IPage<?> page) {
body = PageAdaptter.from(page); body = PageAdapter.from(page);
} }
if (body instanceof String) { if (body instanceof String) {
try { try {
......
...@@ -8,7 +8,7 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver ...@@ -8,7 +8,7 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#最小空闲连接数 #最小空闲连接数
spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.minimum-idle=5
#最大连接池大小 #最大连接池大小
spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.maximum-pool-size=50
#连接超时时间(60秒) #连接超时时间(60秒)
spring.datasource.hikari.connection-timeout=60000 spring.datasource.hikari.connection-timeout=60000
#空闲连接超时时间(600秒) #空闲连接超时时间(600秒)
......
...@@ -5,7 +5,7 @@ server.servlet.context-path=/ ...@@ -5,7 +5,7 @@ server.servlet.context-path=/
## Tomcat配置 ## Tomcat配置
server.tomcat.uri-encoding=UTF-8 server.tomcat.uri-encoding=UTF-8
server.tomcat.accept-count=1000 server.tomcat.accept-count=1000
server.tomcat.threads.max=800 server.tomcat.threads.max=200
server.tomcat.threads.min-spare=100 server.tomcat.threads.min-spare=100
## Spring配置 ## Spring配置
...@@ -35,7 +35,7 @@ server.needAuthentication=true ...@@ -35,7 +35,7 @@ server.needAuthentication=true
token.header=Authorization token.header=Authorization
# 令牌密钥(兼容旧版本) # 令牌密钥(兼容旧版本)
token.secret=custom token.secret=custom
# 令牌有效期(默认30分钟) # 令牌有效期(单位:分钟)
token.expireTime=720 token.expireTime=720
# HTTP Client Configuration # HTTP Client Configuration
......
...@@ -69,7 +69,7 @@ public class CustomProductInfoController { ...@@ -69,7 +69,7 @@ public class CustomProductInfoController {
} }
@Operation(summary = "绑定默认模型", description = "设置商品的默认 DIY 模板") @Operation(summary = "绑定默认模型", description = "设置商品的默认 DIY 模板")
@GetMapping("/bindDefaultDiy") @PostMapping("/bindDefaultDiy")
public void bindDefaultDiy( public void bindDefaultDiy(
@Parameter(description = "商品 ID") @RequestParam Integer id, @Parameter(description = "商品 ID") @RequestParam Integer id,
@Parameter(description = "DIY 模板 ID") @RequestParam Integer diyId, @Parameter(description = "DIY 模板 ID") @RequestParam Integer diyId,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment