Browse Source

11-21 每日获取最新的产品列表

sunjiabei/feature-20251021102635-银行接口
lijianlin 5 days ago
parent
commit
0541172f7a
  1. 2
      src/main/java/com/example/demo/DemoApplication.java
  2. 89
      src/main/java/com/example/demo/Util/ProductRemoteClient.java
  3. 6
      src/main/java/com/example/demo/controller/coin/GeneralController.java
  4. 29
      src/main/java/com/example/demo/domain/DTO/ProductDTO.java
  5. 11
      src/main/java/com/example/demo/mapper/coin/GeneralMapper.java
  6. 2
      src/main/java/com/example/demo/service/coin/GeneralService.java
  7. 48
      src/main/java/com/example/demo/serviceImpl/coin/GeneralServiceImpl.java
  8. 3
      src/main/resources/jindouMapper/BeanConsumeMapper.xml
  9. 33
      src/main/resources/mapper/GeneralMapper.xml

2
src/main/java/com/example/demo/DemoApplication.java

@ -19,7 +19,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
public class DemoApplication {
public static void main(String[] args) {
System.setProperty("https.protocols", "TLSv1");
System.setProperty("https.protocols", "TLSv1,TLSv1.2,TLSv1.3");
SpringApplication.run(DemoApplication.class, args);
}

89
src/main/java/com/example/demo/Util/ProductRemoteClient.java

@ -0,0 +1,89 @@
package com.example.demo.Util;
import com.example.demo.domain.DTO.ProductDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@Component
@RequiredArgsConstructor
public class ProductRemoteClient {
private final RestTemplate restTemplate;
/**
* 获取远端全部商品全量
*/
public List<ProductDTO> fetchAll() {
// 1. 去掉 url 尾部空格
String url = "https://api.homilychart.com/live_mall/api/product/all";
// 2. 构造日志
log.info("[ProductRemote] 开始拉取远端全量商品");
try {
// 3. 指定返回类型避免 Map Raw Type 警告
ResponseEntity<Map> resp =
restTemplate.getForEntity(url, Map.class);
// 4. 状态码校验
if (resp.getStatusCode() != HttpStatus.OK || resp.getBody() == null) {
throw new RuntimeException("远端返回异常,状态码:" + resp.getStatusCode());
}
// 5. 安全取值
Object dataObj = resp.getBody().get("data");
if (!(dataObj instanceof List)) {
throw new RuntimeException("远端 data 字段不是数组");
}
List<Map<String, Object>> data = (List<Map<String, Object>>) dataObj;
// 6. 空数组直接返回避免 NPE
if (data.isEmpty()) {
log.warn("[ProductRemote] 远端返回空数组");
return Collections.emptyList();
}
// 7. 映射 + 空指针保护
long now = System.currentTimeMillis();
return data.stream()
.filter(Objects::nonNull) // 过滤 null 元素
.map(m -> {
ProductDTO dto = new ProductDTO();
dto.setId((Integer) m.get("id"));
dto.setName((String) m.get("name"));
dto.setCover((String) m.get("cover"));
Object priceObj = m.get("price");
dto.setPrice(priceObj != null ? new BigDecimal(priceObj.toString()) : BigDecimal.ZERO);
dto.setUpdatedAt(now);
return dto;
})
.collect(Collectors.toList());
} catch (ResourceAccessException e) {
// 网络/超时异常
log.error("拉取商品失败,真实原因:", e);
throw new RuntimeException("拉取商品失败:" + e.getMessage(), e);
} catch (RestClientException e) {
// 4xx/5xx/解析异常
log.error("[ProductRemote] 远端异常:{}", e.getMessage());
throw new RuntimeException("拉取商品失败:远端返回异常", e);
} catch (Exception e) {
// 兜底
log.error("[ProductRemote] 未知异常", e);
throw new RuntimeException("拉取商品失败:未知错误", e);
}}
}

6
src/main/java/com/example/demo/controller/coin/GeneralController.java

@ -86,4 +86,10 @@ public class GeneralController {
{
return Result.success(generalService.getRate());
}
//手动同步link商品表
@PostMapping("/syncLinkProducts")
public void syncLinkProducts(){
generalService.fullSyncProducts();
}
}

29
src/main/java/com/example/demo/domain/DTO/ProductDTO.java

@ -0,0 +1,29 @@
package com.example.demo.domain.DTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.hpsf.Decimal;
import java.math.BigDecimal;
/**
* @program: gold-java
* @ClassName ProductDTO
* @description:
* @author: Ethan
* @create: 202511-21 10:54
* @Version 1.0
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductDTO {
private Integer id; //id
private String name; //商品名
private String cover;
private BigDecimal price; //价格
private Long updatedAt;
private Long syncTime; //更新
}

11
src/main/java/com/example/demo/mapper/coin/GeneralMapper.java

@ -1,9 +1,13 @@
package com.example.demo.mapper.coin;
import com.example.demo.domain.DTO.ProductDTO;
import com.example.demo.domain.entity.Rate;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* @program: GOLD
@ -31,4 +35,11 @@ public interface GeneralMapper {
List<String> getAllRoleMarket();
List<Rate> getRate();
//查找最新更新时间
Long maxUpdatedAt();
// 批量插入
void replaceBatch(@Param("list") List<ProductDTO> list,
@Param("syncTime") long syncTime);
//删除已删掉的产品
void deleteNotIn(@Param("ids") Collection<Integer> ids);
}

2
src/main/java/com/example/demo/service/coin/GeneralService.java

@ -43,4 +43,6 @@ public interface GeneralService {
List<String> getAllRoleMarket();
//获取汇率
List<Rate> getRate();
void fullSyncProducts();
}

48
src/main/java/com/example/demo/serviceImpl/coin/GeneralServiceImpl.java

@ -1,17 +1,22 @@
package com.example.demo.serviceImpl.coin;
import com.example.demo.Util.ProductRemoteClient;
import com.example.demo.domain.DTO.ProductDTO;
import com.example.demo.domain.entity.Rate;
import com.example.demo.mapper.coin.GeneralMapper;
import com.example.demo.service.coin.GeneralService;
import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -28,8 +33,15 @@ import java.util.stream.Collectors;
public class GeneralServiceImpl implements GeneralService {
private static final Logger log = LoggerFactory.getLogger(GeneralServiceImpl.class);
private final ProductRemoteClient remoteClient;
@Autowired
private GeneralMapper generalMapper;
public GeneralServiceImpl(ProductRemoteClient remoteClient) {
this.remoteClient = remoteClient;
}
@Override
public List<String> getMarket() {
List<String> list = generalMapper.getMarket();
@ -220,4 +232,40 @@ public class GeneralServiceImpl implements GeneralService {
List<Rate> list = generalMapper.getRate();
return list;
}
@Override
@Scheduled(cron = "0 0 2 * * *") // 02:00
public void fullSyncProducts() {
StopWatch sw = StopWatch.createStarted();
log.info("【全量商品同步】开始");
try {
// 1. 拉取远端全量
List<ProductDTO> remoteList = remoteClient.fetchAll();
if (remoteList.isEmpty()) {
log.warn("远端无数据,跳过");
return;
}
Map<Integer, ProductDTO> remoteMap = remoteList.stream()
.collect(Collectors.toMap(ProductDTO::getId, Function.identity()));
// 2. 覆盖本地含新增 & 修改
long syncTime = System.currentTimeMillis();
generalMapper.replaceBatch(remoteList, syncTime);
// 3. 删除本地已下架的品
Set<Integer> remoteIds = remoteMap.keySet();
generalMapper.deleteNotIn(remoteIds);
// 4. 统计
int total = remoteList.size();
log.info("【全量商品同步】完成,远端 {} 条,本地覆盖 {} 条,耗时 {} ms",
total, total, sw.getTime());
} catch (Exception e) {
log.error("【全量商品同步】失败", e);
// 可钉钉/邮件告警
}
}
}

3
src/main/resources/jindouMapper/BeanConsumeMapper.xml

@ -333,6 +333,9 @@
<select id="selectCartBy" resultType="com.example.demo.domain.vo.bean.BeanConsumeCartVo">
select
co.nickname,co.jwcode,m.name AS market,co.product_id,co.total_money,co.live_name,co.source_name,co.trade_time
from cart_order co
left join user u on u.jwcode = co.jwcode
left join market m on m.id = u.market
</select>

33
src/main/resources/mapper/GeneralMapper.xml

@ -1,6 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.coin.GeneralMapper">
<!--批量插入产品-->
<insert id="replaceBatch">
INSERT INTO product_dict
(id, name, cover, price, updated_at, sync_time)
VALUES
<foreach collection="list" item="p" separator=",">
(#{p.id},
#{p.name},
#{p.cover},
#{p.price},
#{p.updatedAt},
#{syncTime})
</foreach>
ON DUPLICATE KEY UPDATE
name = VALUES(name),
cover = VALUES(cover),
price = VALUES(price),
updated_at = VALUES(updated_at),
sync_time = VALUES(sync_time)
</insert>
<!--删除已清除的产品-->
<delete id="deleteNotIn">
DELETE FROM product_dict
WHERE id NOT IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<select id="getMarket" resultType="java.lang.String">
select DISTINCT name from market where type=2
@ -32,5 +60,10 @@
<select id="getRate" resultType="com.example.demo.domain.entity.Rate">
select id,rate_name from rate
</select>
<!--查最新更新时间-->
<select id="maxUpdatedAt" resultType="java.lang.Long">
SELECT IFNULL(MAX(updated_at), 0)
FROM product_dict
</select>
</mapper>
Loading…
Cancel
Save