当前位置: 首页 > news >正文

第三方和审核场景回调还是主动查询

 

博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌

博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+MySQL+Vue等前后端分离项目,可以在左边的分类专栏找到更多项目。《Uniapp项目案例》有几个有uniapp教程,企业实战开发。《微服务实战》专栏是本人的实战经验总结,《Spring家族及微服务系列》专注Spring、SpringMVC、SpringBoot、SpringCloud系列、Nacos等源码解读、热门面试题、架构设计等。除此之外还有不少文章等你来细细品味,更多惊喜等着你哦

🍅uniapp微信小程序🍅面试题软考题免费使用,还可以使用微信支付,扫码加群。由于维护成本问题得不到解决,可能将停止线上维护。

🍅文末获取联系🍅精彩专栏推荐订阅👇🏻👇🏻 不然下次找不到哟

Java项目案例《100套》
https://blog.csdn.net/qq_57756904/category_12173599.html
uniapp小程序《100套》

https://blog.csdn.net/qq_57756904/category_12173599.html

有需求代码永远写不完,而方法才是破解之道,抖音有实战视频课程,某马某千等培训都是2万左右,甚至广东有本科院校单单一年就得3万4年就12万学费,而且还没有包括吃饭的钱。所以很划算了。另外博客左侧有源码阅读专栏,对于求职有很大帮助,当然对于工作也是有指导意义等。在大城市求职,你面试来回一趟多多少少都在12块左右,而且一般不会一次性就通过,还得面试几家。而如果你对源码以及微服务等有深度认识,这无疑给你的面试添砖加瓦更上一层楼。

最后再送一句:最好是学会了,而不是学废了!!

2

我们面临的需求是:第三方推送商品信息到我们平台,我们需要审核后才能上架。第三方想要百分百知道商品是否成功上架,但审核时间不确定。因此,我们需要设计一个机制,让第三方能够准确地得知商品最终的审核状态。

设计思路:

  1. 第三方推送商品数据到我们系统,我们将其保存为待审核状态,并返回一个唯一标识(比如商品ID)给第三方。

  2. 由于审核是人工或异步过程,我们不能立即返回审核结果。因此,我们需要提供一种方式让第三方可以查询商品审核状态。

  3. 另外,我们也可以提供回调通知(webhook)的方式,当审核完成后,主动通知第三方审核结果。

两种方式可以同时提供,也可以根据实际情况选择。下面分别描述:

方式一:提供状态查询接口

  • 第三方在推送商品后,会收到一个商品ID(或审核任务ID)。第三方可以定期使用这个ID来查询审核状态。

方式二:审核结果回调

  • 第三方在推送商品时,需要提供一个回调地址(callback URL)。当审核完成后,我们向这个地址发送审核结果。

考虑到第三方可能两种方式都需要,我们可以同时实现。

具体实现:

  1. 数据库设计:
    商品表(product)需要包含以下字段(至少):

    • id: 商品ID(主键)

    • third_party_id: 第三方商品ID(可选,用于第三方标识自己的商品)

    • name: 商品名称

    • description: 商品描述

    • status: 状态(待审核:PENDING,审核通过:APPROVED,审核拒绝:REJECTED)

    • create_time: 创建时间

    • update_time: 更新时间

    • callback_url: 第三方回调地址(可选,如果使用回调方式则需存储)

  2. 接口设计:

    a. 商品推送接口:
    - 请求方式:POST
    - 路径:/api/product/push
    - 请求体:包含商品信息(如名称、描述等)和回调地址(可选)
    - 响应:返回商品ID(或审核任务ID)和当前状态(一般为PENDING)

    b. 审核状态查询接口:
    - 请求方式:GET
    - 路径:/api/product/{id}/status
    - 响应:返回商品ID和当前状态

    c. 审核处理(内部使用):
    - 提供一个内部接口或管理界面,供审核人员更改商品状态。当状态更改时,如果该商品有回调地址,则触发回调通知。

  3. 回调通知实现:

    • 当商品状态变为审核通过或拒绝时,检查是否有回调地址。如果有,则异步发送HTTP请求到回调地址,通知审核结果。

  4. 注意事项:

    • 回调通知可能会失败,因此需要实现重试机制。例如,使用消息队列来异步处理回调,并记录重试次数,如果多次失败则记录日志并人工处理。

技术实现:

我们使用Spring Boot来构建项目。

步骤:

  1. 创建Product实体类。

  2. 创建ProductRepository,使用JPA或MyBatis等持久层框架。

  3. 创建ProductService,包含推送商品、查询状态、更新审核状态等方法。

  4. 创建ProductController,提供推送接口和状态查询接口。

  5. 创建审核回调的组件,可以使用Spring的异步功能或消息队列(如RabbitMQ、Kafka)来异步发送回调通知。

下面是一个简单的代码框架:

实体类(Product):

针对您描述的场景,我设计一个完整的审核流程方案,包含状态管理和回调机制:

1. 数据库设计

CREATE TABLE product_audit (id BIGINT PRIMARY KEY AUTO_INCREMENT,third_party_id VARCHAR(64) NOT NULL COMMENT '第三方商品ID',product_data JSON NOT NULL COMMENT '商品完整数据',audit_status TINYINT NOT NULL DEFAULT 0 COMMENT '审核状态:0-待审核,1-审核通过,2-审核拒绝',audit_remark VARCHAR(500) COMMENT '审核备注',audit_time DATETIME COMMENT '审核时间',auditor VARCHAR(64) COMMENT '审核人',push_time DATETIME NOT NULL COMMENT '推送时间',push_success TINYINT NOT NULL DEFAULT 1 COMMENT '推送是否成功',callback_status TINYINT NOT NULL DEFAULT 0 COMMENT '回调状态:0-未回调,1-回调成功,2-回调失败',callback_time DATETIME COMMENT '最后一次回调时间',callback_url VARCHAR(500) COMMENT '第三方回调地址',retry_count INT DEFAULT 0 COMMENT '回调重试次数',version INT DEFAULT 0 COMMENT '乐观锁版本',created_time DATETIME DEFAULT CURRENT_TIMESTAMP,updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,INDEX idx_third_party_id (third_party_id),INDEX idx_audit_status (audit_status),INDEX idx_push_time (push_time)
);

2. 核心枚举定义

public enum AuditStatus {PENDING(0, "待审核"),APPROVED(1, "审核通过"),REJECTED(2, "审核拒绝");private final int code;private final String desc;// getter, constructor...
}public enum CallbackStatus {NOT_CALLED(0, "未回调"),SUCCESS(1, "回调成功"),FAILED(2, "回调失败");private final int code;private final String desc;// getter, constructor...
}

3. 实体类

@Entity
@Table(name = "product_audit")
public class ProductAudit {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String thirdPartyId;@Column(columnDefinition = "json")private String productData;private Integer auditStatus;private String auditRemark;private LocalDateTime auditTime;private String auditor;private LocalDateTime pushTime;private Boolean pushSuccess;private Integer callbackStatus;private LocalDateTime callbackTime;private String callbackUrl;private Integer retryCount;@Versionprivate Integer version;// getters and setters...
}

4. 服务层实现

4.1 审核服务

@Service
@Transactional
public class ProductAuditService {@Autowiredprivate ProductAuditRepository auditRepository;@Autowiredprivate CallbackService callbackService;/*** 接收第三方推送*/public PushResult pushProduct(ProductPushRequest request) {ProductAudit audit = new ProductAudit();audit.setThirdPartyId(request.getThirdPartyId());audit.setProductData(JSON.toJSONString(request.getProductData()));audit.setPushTime(LocalDateTime.now());audit.setPushSuccess(true);audit.setCallbackUrl(request.getCallbackUrl());audit.setAuditStatus(AuditStatus.PENDING.getCode());audit.setCallbackStatus(CallbackStatus.NOT_CALLED.getCode());auditRepository.save(audit);return new PushResult(true, "推送成功", audit.getId().toString());}/*** 审核操作*/public void auditProduct(Long auditId, boolean approved, String remark, String auditor) {ProductAudit audit = auditRepository.findById(auditId).orElseThrow(() -> new RuntimeException("审核记录不存在"));if (!AuditStatus.PENDING.getCode().equals(audit.getAuditStatus())) {throw new RuntimeException("当前状态不可审核");}audit.setAuditStatus(approved ? AuditStatus.APPROVED.getCode() : AuditStatus.REJECTED.getCode());audit.setAuditRemark(remark);audit.setAuditor(auditor);audit.setAuditTime(LocalDateTime.now());auditRepository.save(audit);// 异步触发回调callbackService.triggerCallback(audit);}/*** 查询审核状态*/public AuditStatusVO getAuditStatus(String thirdPartyId) {return auditRepository.findByThirdPartyId(thirdPartyId).map(this::convertToVO).orElseThrow(() -> new RuntimeException("商品记录不存在"));}
}

4.2 回调服务

@Service
@Slf4j
public class CallbackService {@Autowiredprivate ProductAuditRepository auditRepository;@Autowiredprivate RestTemplate restTemplate;@Async("callbackExecutor")public void triggerCallback(ProductAudit audit) {if (StringUtils.isEmpty(audit.getCallbackUrl())) {return;}CallbackRequest callbackRequest = buildCallbackRequest(audit);boolean success = doCallback(audit.getCallbackUrl(), callbackRequest);updateCallbackStatus(audit, success);}private boolean doCallback(String callbackUrl, CallbackRequest request) {int maxRetries = 3;for (int i = 0; i < maxRetries; i++) {try {ResponseEntity<String> response = restTemplate.postForEntity(callbackUrl, request, String.class);if (response.getStatusCode().is2xxSuccessful()) {log.info("回调成功: {}", callbackUrl);return true;}} catch (Exception e) {log.warn("回调失败, 第{}次重试: {}", i + 1, e.getMessage());if (i < maxRetries - 1) {try {Thread.sleep(2000 * (i + 1)); // 递增重试间隔} catch (InterruptedException ie) {Thread.currentThread().interrupt();}}}}return false;}@Transactionalpublic void updateCallbackStatus(ProductAudit audit, boolean success) {auditRepository.updateCallbackStatus(audit.getId(),success ? CallbackStatus.SUCCESS.getCode() : CallbackStatus.FAILED.getCode(),LocalDateTime.now(),audit.getRetryCount() + 1);}
}

5. 控制器层

@RestController
@RequestMapping("/api/product")
@Validated
public class ProductAuditController {@Autowiredprivate ProductAuditService auditService;/*** 第三方推送商品接口*/@PostMapping("/push")public Result<PushResult> pushProduct(@Valid @RequestBody ProductPushRequest request) {PushResult result = auditService.pushProduct(request);return Result.success(result);}/*** 状态查询接口*/@GetMapping("/status/{thirdPartyId}")public Result<AuditStatusVO> getStatus(@PathVariable String thirdPartyId) {AuditStatusVO status = auditService.getAuditStatus(thirdPartyId);return Result.success(status);}/*** 内部审核接口*/@PostMapping("/audit")public Result<Void> auditProduct(@Valid @RequestBody AuditRequest request) {auditService.auditProduct(request.getAuditId(), request.isApproved(), request.getRemark(), request.getAuditor());return Result.success();}
}

6. 回调请求体

@Data
public class CallbackRequest {private String thirdPartyId;private Integer auditStatus;private String auditRemark;private LocalDateTime auditTime;private Long auditRecordId;private String sign; // 签名,用于安全验证
}

7. 配置类

@Configuration
@EnableAsync
public class AsyncConfig {@Bean("callbackExecutor")public Executor callbackExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(100);executor.setThreadNamePrefix("callback-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}

8. 定时任务处理回调失败

@Component
@Slf4j
public class CallbackRetryTask {@Autowiredprivate ProductAuditRepository auditRepository;@Autowiredprivate CallbackService callbackService;/*** 每5分钟执行一次,重试失败的回调*/@Scheduled(fixedRate = 300000)public void retryFailedCallbacks() {List<ProductAudit> failedAudits = auditRepository.findByCallbackStatusAndRetryCountLessThan(CallbackStatus.FAILED.getCode(), 5);for (ProductAudit audit : failedAudits) {log.info("重试回调: {}", audit.getThirdPartyId());callbackService.triggerCallback(audit);}}
}

9. 第三方集成方式

方式一:主动查询(推荐)

// 第三方在推送后定期查询状态
@Scheduled(fixedRate = 60000) // 每分钟查询一次
public void checkAuditStatus() {// 调用 GET /api/product/status/{thirdPartyId}
}

方式二:被动接收回调

// 第三方提供回调接口
@PostMapping("/third-party/callback")
public Result<Void> handleAuditCallback(@RequestBody CallbackRequest request) {// 验证签名if (!verifySign(request)) {return Result.error("签名验证失败");}// 处理审核结果processAuditResult(request);return Result.success();
}

系统特点

  1. 状态明确:清晰区分推送成功和审核成功

  2. 异步处理:审核和回调异步执行,不影响主流程

  3. 重试机制:失败回调自动重试,提高可靠性

  4. 可追溯:完整记录审核流程和操作日志

  5. 灵活集成:支持主动查询和被动回调两种方式

  6. 线程安全:使用乐观锁防止并发问题

这样设计既满足了审核流程的不确定性需求,又保证了第三方能够及时准确地获取审核结果。

http://www.dtcms.com/a/473901.html

相关文章:

  • Git基本命令的使用(超详细)
  • NC40 链表相加(二)
  • 网安面试题收集(3)
  • JetLinks设备接入的认识与理解
  • 从HashMap到ConcurrentHashMap深入剖析Java并发容器的演进与实战
  • 做一组静态页面网站多少钱网站源码上传到哪个文件夹
  • 威海市城乡建设局网站网络整合营销服务商
  • 从报头到路由器——【网络编程】详解 IP 协议:报头字段、路由器功能、网段划分和分片传输
  • 网站验证北京建网站开发
  • 设计模式篇之 装饰器模式 Decorator
  • 虚幻引擎虚拟制片入门教程 之 创建项目及启用插件
  • 淳安县建设网站王璞网站开发实战答案
  • Linux禁用自带键盘和触摸板(无需每次开机重置)
  • 149、【OS】【Nuttx】【周边】效果呈现方案解析:VSCode 打开外部链接(二)
  • Apache Commons IO:文件流处理利器,让Java IO操作更简单
  • 哪个网站做简历免费自己做免费网站
  • 医院预约挂号|基于Java+vue的医院预约挂号系统小程序的设计与实现(源码+数据库+文档)
  • 翻转二叉树---超全详细解
  • AI智能体全球应用调查报告:从“对话”到“做事”的变革
  • Linux网络之----网络编程
  • [Power BI] CALCULATETABLE函数
  • 3494. 酿造药水需要的最少总时间
  • 沐风老师3DMAX科研绘图插件DNA生成器使用方法详解
  • 宁波做网站gs什么是网络营销的职能
  • AI编程工具(Cursor/Copilot/灵码/文心一言/Claude Code/Trae)AI编程辅助工具全方位比较
  • FastGPT入门实战
  • 数据结构笔试核心考点
  • 用python做购物网站万网搜官网
  • 创建qq网站如何做网站流量分析报表
  • Docker实战:从基础镜像到Nginx定制