Commit 31b0549455d22c2a86909bba96ce7703b8d799ad

Authored by zhangmeiyang
1 parent c880414c

feat(storage): 新增税务管道映射功能及相关服务

- 添加 DynamicTaxPipelineMappingController 控制器,提供分页查询接口
- 实现 DynamicTaxPipelineMappingService 服务类,支持创建租户映射表、查询管道数据等功能
- 新增 TaxPipelineMappingCO 和 TaxPipelineMappingCreateCO 数据传输对象
- 定义 MappingStateType 枚举,表示映射状态(已同步、同步失败、同步重试)
- 增加 GlobalExceptionHandler 统一异常处理机制- 扩展 ITaxPipelineMappingService 接口定义- 在 TaxPipelineConfigController 和 TaxPipelineController 中添加 @RebuildIndex 注解
- 修改 TaxPipelineCO、TaxPipelineConfigCO、TaxTenantCO 验证组配置
- 更新 TaxPipelineMappingRepository 接口及 XML 映射文件,支持动态表操作
- 添加 RebuildIndexAspect 切面类,用于发布 RestoreTenantEvent 事件
- 引入 spring-boot-starter-aop依赖以支持切面编程
- 调整 PageQuery 类,增加分页参数验证规则
-优化 TaxTenantService,在保存租户时自动创建映射表- 添加 TenantTaxPipelineMapping 模型类用于中央模块使用
Showing 26 changed files with 802 additions and 45 deletions
tax-central/src/main/java/com/diligrp/tax/central/model/TenantTaxPipelineMapping.java 0 → 100644
  1 +package com.diligrp.tax.central.model;
  2 +
  3 +import lombok.Getter;
  4 +import lombok.Setter;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Getter
  9 +@Setter
  10 +public class TenantTaxPipelineMapping {
  11 + /**
  12 + * id
  13 + */
  14 + private Long id;
  15 + /**
  16 + * 管道 ID
  17 + */
  18 + private Long pipelineId;
  19 + /**
  20 + * 文档类型
  21 + */
  22 + private String documentType;
  23 + /**
  24 + * 第三方系统数据 ID
  25 + */
  26 + private String systemDataId;
  27 + /**
  28 + * 所在账套数据 ID
  29 + */
  30 + private String pipelineDataId;
  31 + /**
  32 + * 原产地数据
  33 + */
  34 + private String originData;
  35 + /**
  36 + * 州
  37 + */
  38 + private Integer state;
  39 + /**
  40 + * 创建时间
  41 + */
  42 + private LocalDateTime createdTime;
  43 + /**
  44 + * 修改时间
  45 + */
  46 + private LocalDateTime modifiedTime;
  47 +}
... ...
tax-central/src/main/java/com/diligrp/tax/central/service/ITaxPipelineMappingService.java 0 → 100644
  1 +package com.diligrp.tax.central.service;
  2 +
  3 +import com.diligrp.tax.central.model.TenantTaxPipelineMapping;
  4 +
  5 +import java.util.Optional;
  6 +
  7 +public interface ITaxPipelineMappingService {
  8 +
  9 + /**
  10 + * 按管道 ID 和文档类型以及系统数据 ID 查找
  11 + *
  12 + * @param tenantId 租户 ID
  13 + * @param pipelineId 管道 ID
  14 + * @param documentType 文档类型
  15 + * @param systemDataId 系统数据 ID
  16 + * @return {@link Optional }<{@link TenantTaxPipelineMapping }>
  17 + */
  18 + Optional<TenantTaxPipelineMapping> findByPipelineIdAndDocumentTypeAndSystemDataId(Long tenantId,Long pipelineId,String documentType,String systemDataId);
  19 +}
... ...
tax-storage/pom.xml
... ... @@ -25,5 +25,9 @@
25 25 <groupId>com.baomidou</groupId>
26 26 <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
27 27 </dependency>
  28 + <dependency>
  29 + <groupId>org.springframework.boot</groupId>
  30 + <artifactId>spring-boot-starter-aop</artifactId>
  31 + </dependency>
28 32 </dependencies>
29 33 </project>
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/GlobalExceptionHandler.java 0 → 100644
  1 +package com.diligrp.tax.storage;
  2 +
  3 +import com.diligrp.tax.central.exception.TaxAgentServiceException;
  4 +import com.diligrp.tax.central.type.TaxSystemType;
  5 +import com.diligrp.tax.storage.message.Message;
  6 +import com.fasterxml.jackson.databind.exc.InvalidFormatException;
  7 +import jakarta.validation.ConstraintViolation;
  8 +import jakarta.validation.ConstraintViolationException;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.context.support.DefaultMessageSourceResolvable;
  11 +import org.springframework.http.converter.HttpMessageNotReadableException;
  12 +import org.springframework.validation.BindException;
  13 +import org.springframework.validation.BindingResult;
  14 +import org.springframework.validation.ObjectError;
  15 +import org.springframework.web.bind.MethodArgumentNotValidException;
  16 +import org.springframework.web.bind.MissingServletRequestParameterException;
  17 +import org.springframework.web.bind.annotation.ControllerAdvice;
  18 +import org.springframework.web.bind.annotation.ExceptionHandler;
  19 +
  20 +import java.util.List;
  21 +import java.util.Set;
  22 +import java.util.stream.Collectors;
  23 +
  24 +/**
  25 + * 统一异常处理器
  26 + */
  27 +@ControllerAdvice
  28 +@Slf4j
  29 +public class GlobalExceptionHandler {
  30 + /**
  31 + * 处理业务异常
  32 + */
  33 + @ExceptionHandler(TaxAgentServiceException.class)
  34 + public Message<?> handleBusinessException(TaxAgentServiceException e) {
  35 + log.error("Method throw TaxAgentServiceException", e);
  36 + return Message.failure(e.getCode(), e.getMessage());
  37 + }
  38 +
  39 + /**
  40 + * 统一拦截参数转换异常 JSON -> Object
  41 + *
  42 + * @param ex InvalidFormatException
  43 + * @return 响应消息对象
  44 + */
  45 + @ExceptionHandler({HttpMessageNotReadableException.class, InvalidFormatException.class})
  46 + public Message<?> invalidFormatExceptionHandler(Exception ex) {
  47 + return Message.failure(TaxSystemType.ABNORMAL_PARAMETERS.code, ex.getMessage());
  48 + }
  49 +
  50 + /**
  51 + * 参数验证
  52 + *
  53 + * @param exs 参数异常
  54 + * @return BaseOutput
  55 + */
  56 + @ExceptionHandler(value = ConstraintViolationException.class)
  57 + public Message<?> constraintViolationException(ConstraintViolationException exs) {
  58 + Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
  59 + String msg = violations.stream().map(ConstraintViolation::getMessage)
  60 + .collect(Collectors.joining(","));
  61 + return Message.failure(TaxSystemType.ABNORMAL_PARAMETERS.code, msg);
  62 + }
  63 +
  64 + /**
  65 + * 参数验证
  66 + *
  67 + * @param e MethodArgumentNotValidException @RequestBody
  68 + * BindException 普通form表单
  69 + * @return BaseOutput
  70 + */
  71 + @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
  72 + public Message<?> methodArgumentNotValidException(Exception e) {
  73 + BindingResult bindingResult;
  74 + if (e instanceof MethodArgumentNotValidException) {
  75 + bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
  76 + } else {
  77 + bindingResult = ((BindException) e).getBindingResult();
  78 + }
  79 + List<ObjectError> allErrors = bindingResult.getAllErrors();
  80 + String msg = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
  81 + .collect(Collectors.joining("、"));
  82 +
  83 + return Message.failure(TaxSystemType.ABNORMAL_PARAMETERS.code, msg);
  84 + }
  85 +
  86 + /**
  87 + * 缺少请求参数异常
  88 + *
  89 + * @param ex 请求参数异常
  90 + * @return 异常信息
  91 + */
  92 + @ExceptionHandler(MissingServletRequestParameterException.class)
  93 + public Message<?> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
  94 + return Message.failure(TaxSystemType.ABNORMAL_PARAMETERS.code, String.format("缺少参数:%s", ex.getParameterName()));
  95 + }
  96 +
  97 + /**
  98 + * 断言验证
  99 + *
  100 + * @param ex IllegalArgumentException
  101 + * @return BaseOutput
  102 + */
  103 + @ExceptionHandler(value = IllegalArgumentException.class)
  104 + public Message<?> illegalArgumentException(IllegalArgumentException ex) {
  105 + return Message.failure(TaxSystemType.ABNORMAL_PARAMETERS.code, ex.getMessage());
  106 + }
  107 +
  108 + /**
  109 + * 统一拦截系统其它异常
  110 + *
  111 + * @param ex 系统异常
  112 + * @return 响应消息对象
  113 + */
  114 + @ExceptionHandler(Exception.class)
  115 + public Message<?> exceptionHandler(Exception ex) {
  116 + log.error("unknown error :", ex);
  117 + return Message.failure(TaxSystemType.UNKNOWN_ERROR.code, TaxSystemType.UNKNOWN_ERROR.message);
  118 + }
  119 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/Valid.java 0 → 100644
  1 +package com.diligrp.tax.storage;
  2 +
  3 +/**
  4 + * 标记接口
  5 + *
  6 + * @author zhangmeiyang
  7 + * @date 2025/11/07
  8 + */
  9 +public interface Valid {
  10 + interface Create {
  11 + }
  12 +
  13 + interface Read {
  14 + }
  15 +
  16 + interface Update {
  17 + }
  18 +
  19 + interface Delete {
  20 + }
  21 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/anno/RebuildIndex.java 0 → 100644
  1 +package com.diligrp.tax.storage.anno;
  2 +
  3 +import java.lang.annotation.ElementType;
  4 +import java.lang.annotation.Retention;
  5 +import java.lang.annotation.RetentionPolicy;
  6 +import java.lang.annotation.Target;
  7 +
  8 +@Target(ElementType.METHOD)
  9 +@Retention(RetentionPolicy.RUNTIME)
  10 +public @interface RebuildIndex {
  11 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/anno/RebuildIndexAspect.java 0 → 100644
  1 +package com.diligrp.tax.storage.anno;
  2 +
  3 +import com.diligrp.tax.central.event.RestoreTenantEvent;
  4 +import jakarta.annotation.Resource;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.aspectj.lang.annotation.After;
  7 +import org.aspectj.lang.annotation.Aspect;
  8 +import org.springframework.context.ApplicationEventPublisher;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +/**
  12 + * @Author: zhangmeiyang
  13 + * @CreateTime: 2025-11-07 14:06
  14 + * @Version: todo
  15 + */
  16 +@Aspect
  17 +@Component
  18 +@Slf4j
  19 +public class RebuildIndexAspect {
  20 +
  21 + @Resource
  22 + private ApplicationEventPublisher publisher;
  23 +
  24 + /**
  25 + * 防重
  26 + *
  27 + * @return
  28 + * @date 2020/8/12
  29 + */
  30 + @After("@annotation(com.diligrp.tax.storage.anno.RebuildIndex)")
  31 + public void rebuildIndex() {
  32 + log.info("开始重建缓存");
  33 + publisher.publishEvent(new RestoreTenantEvent(this));
  34 + }
  35 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/controller/DynamicTaxPipelineMappingController.java 0 → 100644
  1 +package com.diligrp.tax.storage.controller;
  2 +
  3 +import com.diligrp.tax.storage.Valid;
  4 +import com.diligrp.tax.storage.message.Message;
  5 +import com.diligrp.tax.storage.model.co.TaxPipelineMappingCO;
  6 +import com.diligrp.tax.storage.service.DynamicTaxPipelineMappingService;
  7 +import jakarta.annotation.Resource;
  8 +import org.springframework.validation.annotation.Validated;
  9 +import org.springframework.web.bind.annotation.RequestBody;
  10 +import org.springframework.web.bind.annotation.RequestMapping;
  11 +import org.springframework.web.bind.annotation.RestController;
  12 +
  13 +/**
  14 + * @Author: zhangmeiyang
  15 + * @CreateTime: 2025-11-07 11:14
  16 + * @Version: todo
  17 + */
  18 +@RestController
  19 +@RequestMapping("/tax/tenant/pipeline/mapping")
  20 +public class DynamicTaxPipelineMappingController {
  21 +
  22 + @Resource
  23 + private DynamicTaxPipelineMappingService dynamicTaxPipelineMappingService;
  24 +
  25 + @RequestMapping("page")
  26 + public Message<?> page(@RequestBody @Validated(value = {Valid.Read.class}) TaxPipelineMappingCO taxPipelineMappingCO) {
  27 + return Message.success(dynamicTaxPipelineMappingService.findTenantPipelineData(taxPipelineMappingCO));
  28 + }
  29 +
  30 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/controller/TaxPipelineConfigController.java
1 1 package com.diligrp.tax.storage.controller;
2 2  
  3 +import com.diligrp.tax.storage.Valid;
  4 +import com.diligrp.tax.storage.anno.RebuildIndex;
3 5 import com.diligrp.tax.storage.message.Message;
4 6 import com.diligrp.tax.storage.model.co.TaxPipelineConfigCO;
5 7 import com.diligrp.tax.storage.service.TaxPipelineConfigService;
... ... @@ -54,18 +56,33 @@ public class TaxPipelineConfigController {
54 56 * @return {@link Message }<{@link ? }>
55 57 */
56 58 @RequestMapping("/save")
57   - public Message<?> save(@RequestBody @Validated TaxPipelineConfigCO taxPipelineConfig) {
  59 + @RebuildIndex
  60 + public Message<?> save(@RequestBody @Validated(value = Valid.Create.class) TaxPipelineConfigCO taxPipelineConfig) {
58 61 taxPipelineConfigService.save(taxPipelineConfig);
59 62 return Message.success();
60 63 }
61 64  
62 65 /**
  66 + * 更新
  67 + *
  68 + * @param taxPipelineConfig 税务管道配置
  69 + * @return {@link Message }<{@link ? }>
  70 + */
  71 + @RequestMapping("/update")
  72 + @RebuildIndex
  73 + public Message<?> update(@RequestBody @Validated(value = {Valid.Update.class}) TaxPipelineConfigCO taxPipelineConfig) {
  74 + taxPipelineConfigService.update(taxPipelineConfig);
  75 + return Message.success();
  76 + }
  77 +
  78 + /**
63 79 * 删除
64 80 *
65 81 * @param id id
66 82 * @return {@link Message }<{@link ? }>
67 83 */
68 84 @RequestMapping("/delete/{id}")
  85 + @RebuildIndex
69 86 public Message<?> delete(@PathVariable("id") Long id) {
70 87 taxPipelineConfigService.delete(id);
71 88 return Message.success();
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/controller/TaxPipelineController.java
1 1 package com.diligrp.tax.storage.controller;
2 2  
  3 +import com.diligrp.tax.storage.Valid;
  4 +import com.diligrp.tax.storage.anno.RebuildIndex;
3 5 import com.diligrp.tax.storage.message.Message;
4 6 import com.diligrp.tax.storage.model.co.TaxPipelineCO;
5 7 import com.diligrp.tax.storage.service.TaxPipelineService;
... ... @@ -30,12 +32,26 @@ public class TaxPipelineController {
30 32 * @return {@link Message }<{@link ? }>
31 33 */
32 34 @RequestMapping("/save")
33   - public Message<?> save(@RequestBody @Validated TaxPipelineCO taxPipeline) {
  35 + @RebuildIndex
  36 + public Message<?> save(@RequestBody @Validated(value = Valid.Create.class) TaxPipelineCO taxPipeline) {
34 37 taxPipelineService.save(taxPipeline);
35 38 return Message.success();
36 39 }
37 40  
38 41 /**
  42 + * 保存账套
  43 + *
  44 + * @param taxPipeline 税务管道
  45 + * @return {@link Message }<{@link ? }>
  46 + */
  47 + @RequestMapping("/update")
  48 + @RebuildIndex
  49 + public Message<?> update(@RequestBody @Validated(value = Valid.Update.class) TaxPipelineCO taxPipeline) {
  50 + taxPipelineService.update(taxPipeline);
  51 + return Message.success();
  52 + }
  53 +
  54 + /**
39 55 * 按租户列出
40 56 *
41 57 * @param group 群
... ... @@ -66,6 +82,7 @@ public class TaxPipelineController {
66 82 * @return {@link Message }<{@link ? }>
67 83 */
68 84 @RequestMapping("/disable/{id}")
  85 + @RebuildIndex
69 86 public Message<?> disable(@PathVariable("id") Long id) {
70 87 taxPipelineService.updateState(id, StateType.DISABLE);
71 88 return Message.success();
... ... @@ -78,6 +95,7 @@ public class TaxPipelineController {
78 95 * @return {@link Message }<{@link ? }>
79 96 */
80 97 @RequestMapping("/enable/{id}")
  98 + @RebuildIndex
81 99 public Message<?> enable(@PathVariable("id") Long id) {
82 100 taxPipelineService.updateState(id, StateType.ENABLE);
83 101 return Message.success();
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/controller/TaxTenantController.java
1 1 package com.diligrp.tax.storage.controller;
2 2  
  3 +import com.diligrp.tax.storage.Valid;
  4 +import com.diligrp.tax.storage.anno.RebuildIndex;
3 5 import com.diligrp.tax.storage.message.Message;
4 6 import com.diligrp.tax.storage.model.co.TaxTenantCO;
5 7 import com.diligrp.tax.storage.service.TaxTenantService;
... ... @@ -42,19 +44,33 @@ public class TaxTenantController {
42 44 * @return {@link Message }<{@link ? }>
43 45 */
44 46 @RequestMapping("/save")
45   - public Message<?> save(@RequestBody @Validated TaxTenantCO taxTenantCO) {
  47 + @RebuildIndex
  48 + public Message<?> save(@RequestBody @Validated(value = {Valid.Create.class}) TaxTenantCO taxTenantCO) {
46 49 taxTenantService.saveTenant(taxTenantCO);
47 50 return Message.success();
48 51 }
49 52  
50 53 /**
  54 + * 保存
  55 + *
  56 + * @param taxTenantCO 税务租户做
  57 + * @return {@link Message }<{@link ? }>
  58 + */
  59 + @RequestMapping("/update")
  60 + @RebuildIndex
  61 + public Message<?> update(@RequestBody @Validated(value = {Valid.Update.class}) TaxTenantCO taxTenantCO) {
  62 + taxTenantService.updateTenant(taxTenantCO);
  63 + return Message.success();
  64 + }
  65 +
  66 + /**
51 67 * 分页查询
52 68 *
53 69 * @param taxTenantCO 税务租户做
54 70 * @return {@link Message }<{@link ? }>
55 71 */
56 72 @RequestMapping("page")
57   - public Message<?> page(@RequestBody TaxTenantCO taxTenantCO) {
  73 + public Message<?> page(@RequestBody @Validated(value = {Valid.Read.class}) TaxTenantCO taxTenantCO) {
58 74 return Message.success(taxTenantService.findByNameAndState(taxTenantCO));
59 75 }
60 76 }
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/domain/TaxPipelineMapping.java
1 1 package com.diligrp.tax.storage.domain;
2 2  
3   -import com.baomidou.mybatisplus.annotation.IdType;
4   -import com.baomidou.mybatisplus.annotation.TableField;
5   -import com.baomidou.mybatisplus.annotation.TableId;
6   -import com.baomidou.mybatisplus.annotation.TableName;
7   -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
8 3 import lombok.Getter;
9 4 import lombok.Setter;
10 5  
11 6 import java.time.LocalDateTime;
12   -import java.util.Map;
13 7  
14 8 @Getter
15 9 @Setter
16   -@TableName(value = "tax_pipeline_mapping")
17 10 public class TaxPipelineMapping {
18 11  
19   - @TableId(type = IdType.AUTO)
  12 + /**
  13 + * 租户 ID
  14 + */
  15 + private Long tenantId;
  16 +
  17 + /**
  18 + * id
  19 + */
20 20 private Long id;
21 21  
22   - @TableField(value = "pipeline_id")
  22 + /**
  23 + * 管道 ID
  24 + */
23 25 private Long pipelineId;
24 26  
25   - @TableField(value = "document_type")
  27 + /**
  28 + * 文档类型
  29 + */
26 30 private String documentType;
27 31  
28   - @TableField(value = "system_data_id")
  32 + /**
  33 + * 第三方系统数据 ID
  34 + */
29 35 private String systemDataId;
30 36  
31   - @TableField(value = "pipeline_data_id")
  37 + /**
  38 + * 所在账套数据 ID
  39 + */
32 40 private String pipelineDataId;
33 41  
34   - @TableField(value = "origin_data", typeHandler = JacksonTypeHandler.class)
35   - private Map<String, Object> originData;
  42 + /**
  43 + * 原产地数据
  44 + */
  45 + private String originData;
36 46  
37   - @TableField(value = "state")
  47 + /**
  48 + * 州
  49 + */
38 50 private Integer state;
39 51  
40   - @TableField(value = "created_time")
  52 + /**
  53 + * 创建时间
  54 + */
41 55 private LocalDateTime createdTime;
42 56  
43   - @TableField(value = "modified_time")
  57 + /**
  58 + * 修改时间
  59 + */
44 60 private LocalDateTime modifiedTime;
45   -
46 61 }
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/message/PageQuery.java
1 1 package com.diligrp.tax.storage.message;
2 2  
  3 +import com.diligrp.tax.storage.Valid;
  4 +import jakarta.validation.constraints.NotNull;
3 5 import lombok.Getter;
4 6 import lombok.Setter;
5 7  
... ... @@ -14,9 +16,11 @@ public abstract class PageQuery {
14 16 /**
15 17 * 页码
16 18 */
  19 + @NotNull(groups = {Valid.Read.class})
17 20 protected Integer pageNumber;
18 21 /**
19 22 * 页面大小
20 23 */
  24 + @NotNull(groups = {Valid.Read.class})
21 25 protected Integer pageSize;
22 26 }
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/model/co/TaxPipelineCO.java
1 1 package com.diligrp.tax.storage.model.co;
2 2  
  3 +import com.diligrp.tax.storage.Valid;
3 4 import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
4 6 import lombok.Getter;
5 7 import lombok.Setter;
6 8  
... ... @@ -17,6 +19,7 @@ public class TaxPipelineCO {
17 19 /**
18 20 * id
19 21 */
  22 + @NotNull(groups = Valid.Update.class)
20 23 private Long id;
21 24 /**
22 25 * 租户 ID
... ... @@ -25,32 +28,32 @@ public class TaxPipelineCO {
25 28 /**
26 29 * 群
27 30 */
28   - @NotEmpty
  31 + @NotEmpty(groups = Valid.Create.class)
29 32 private String group;
30 33 /**
31 34 * 实体
32 35 */
33   - @NotEmpty
  36 + @NotEmpty(groups = Valid.Create.class)
34 37 private String entity;
35 38 /**
36 39 * 账套类型
37 40 */
38   - @NotEmpty
  41 + @NotEmpty(groups = {Valid.Create.class})
39 42 private String systemCode;
40 43 /**
41 44 * 账套名字
42 45 */
43   - @NotEmpty
  46 + @NotEmpty(groups = Valid.Create.class)
44 47 private String name;
45 48 /**
46 49 * 账套编码
47 50 */
48   - @NotEmpty
  51 + @NotEmpty(groups = Valid.Create.class)
49 52 private String code;
50 53 /**
51 54 * 参数
52 55 */
53   - @NotEmpty
  56 + @NotEmpty(groups = Valid.Create.class)
54 57 private Map<String, Object> params;
55 58 /**
56 59 * 状态
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/model/co/TaxPipelineConfigCO.java
1 1 package com.diligrp.tax.storage.model.co;
2 2  
  3 +import com.diligrp.tax.storage.Valid;
3 4 import jakarta.validation.constraints.NotEmpty;
4 5 import jakarta.validation.constraints.NotNull;
5 6 import lombok.Getter;
... ... @@ -12,30 +13,31 @@ public class TaxPipelineConfigCO {
12 13 /**
13 14 * id
14 15 */
  16 + @NotNull(groups = {Valid.Update.class, Valid.Delete.class})
15 17 private Long id;
16 18  
17 19 /**
18 20 * 管道 ID
19 21 */
20   - @NotNull
  22 + @NotNull(groups = {Valid.Create.class})
21 23 private Long pipelineId;
22 24  
23 25 /**
24 26 * 文档类型
25 27 */
26   - @NotEmpty
  28 + @NotEmpty(groups = {Valid.Create.class})
27 29 private String documentType;
28 30  
29 31 /**
30 32 * 配置键
31 33 */
32   - @NotEmpty
  34 + @NotEmpty(groups = {Valid.Create.class})
33 35 private String configKey;
34 36  
35 37 /**
36 38 * 配置值
37 39 */
38   - @NotEmpty
  40 + @NotEmpty(groups = {Valid.Create.class})
39 41 private String configValue;
40 42  
41 43 }
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/model/co/TaxPipelineMappingCO.java 0 → 100644
  1 +package com.diligrp.tax.storage.model.co;
  2 +
  3 +import com.diligrp.tax.storage.Valid;
  4 +import com.diligrp.tax.storage.message.PageQuery;
  5 +import jakarta.validation.constraints.NotEmpty;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Getter;
  8 +import lombok.Setter;
  9 +
  10 +@Getter
  11 +@Setter
  12 +public class TaxPipelineMappingCO extends PageQuery {
  13 +
  14 + /**
  15 + * 分组
  16 + */
  17 + @NotEmpty(groups = {Valid.Read.class})
  18 + private String group;
  19 +
  20 + /**
  21 + * 实体
  22 + */
  23 + @NotEmpty(groups = {Valid.Read.class})
  24 + private String entity;
  25 +
  26 + /**
  27 + * 租户 ID
  28 + */
  29 + private Long tenantId;
  30 + /**
  31 + * 管道 ID
  32 + */
  33 + @NotNull(groups = {Valid.Read.class})
  34 + private Long pipelineId;
  35 + /**
  36 + * 文档类型
  37 + */
  38 + private String documentType;
  39 + /**
  40 + * 第三方系统数据 ID
  41 + */
  42 + private String systemDataId;
  43 + /**
  44 + * 州
  45 + */
  46 + private Integer state;
  47 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/model/co/TaxPipelineMappingCreateCO.java 0 → 100644
  1 +package com.diligrp.tax.storage.model.co;
  2 +
  3 +import jakarta.validation.Valid;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Getter;
  7 +import lombok.Setter;
  8 +
  9 +/**
  10 + * @Author: zhangmeiyang
  11 + * @CreateTime: 2025-11-07 11:06
  12 + * @Version: todo
  13 + */
  14 +@Getter
  15 +@Setter
  16 +@Valid
  17 +public class TaxPipelineMappingCreateCO {
  18 + /**
  19 + * 租户 ID
  20 + */
  21 + @NotNull(message = "租户ID不能为空")
  22 + private Long tenantId;
  23 + /**
  24 + * 管道 ID
  25 + */
  26 + @NotNull(message = "管道ID不能为空")
  27 + private Long pipelineId;
  28 + /**
  29 + * 文档类型
  30 + */
  31 + @NotEmpty(message = "文档类型不能为空")
  32 + private String documentType;
  33 + /**
  34 + * 同步系统数据唯一键
  35 + */
  36 + @NotEmpty(message = "同步系统数据唯一键不能为空")
  37 + private String systemDataId;
  38 + /**
  39 + * 账套数据唯一键
  40 + */
  41 + @NotEmpty(message = "账套数据唯一键不能为空")
  42 + private String pipelineDataId;
  43 + /**
  44 + * 原始发送数据
  45 + */
  46 + @NotEmpty(message = "原始发送数据不能为空")
  47 + private String originData;
  48 + /**
  49 + * 同步状态
  50 + */
  51 + @NotNull(message = "同步状态不能为空")
  52 + private Integer state;
  53 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/model/co/TaxTenantCO.java
1 1 package com.diligrp.tax.storage.model.co;
2 2  
  3 +import com.diligrp.tax.storage.Valid;
3 4 import com.diligrp.tax.storage.message.PageQuery;
4 5 import jakarta.validation.constraints.NotEmpty;
  6 +import jakarta.validation.constraints.NotNull;
5 7 import lombok.Getter;
6 8 import lombok.Setter;
7 9  
... ... @@ -16,21 +18,22 @@ public class TaxTenantCO extends PageQuery {
16 18 /**
17 19 * id
18 20 */
  21 + @NotNull(groups = {Valid.Update.class, Valid.Delete.class, Valid.Read.class})
19 22 private Long id;
20 23 /**
21 24 * 名字
22 25 */
23   - @NotEmpty
  26 + @NotEmpty(groups = {Valid.Create.class,Valid.Update.class})
24 27 private String name;
25 28 /**
26 29 * 群
27 30 */
28   - @NotEmpty
  31 + @NotEmpty(groups = {Valid.Create.class,Valid.Update.class})
29 32 private String group;
30 33 /**
31 34 * 实体
32 35 */
33   - @NotEmpty
  36 + @NotEmpty(groups = {Valid.Create.class,Valid.Update.class})
34 37 private String entity;
35 38 /**
36 39 * 状态
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/repo/TaxPipelineMappingRepository.java
1 1 package com.diligrp.tax.storage.repo;
2 2  
3   -import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3 +import com.baomidou.mybatisplus.core.metadata.IPage;
  4 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
4 5 import com.diligrp.tax.storage.domain.TaxPipelineMapping;
  6 +import com.diligrp.tax.central.model.TenantTaxPipelineMapping;
  7 +import org.apache.ibatis.annotations.Param;
5 8 import org.springframework.stereotype.Repository;
6 9  
  10 +import java.util.List;
  11 +import java.util.Optional;
  12 +
7 13 @Repository
8   -public interface TaxPipelineMappingRepository extends BaseMapper<TaxPipelineMapping> {
  14 +public interface TaxPipelineMappingRepository {
  15 +
  16 + /**
  17 + * 插入
  18 + *
  19 + * @param mapping 税务管道映射
  20 + */
  21 + void insert(TaxPipelineMapping mapping);
  22 +
  23 + /**
  24 + * 更新
  25 + *
  26 + * @param mapping 税务管道映射
  27 + */
  28 + void update(TaxPipelineMapping mapping);
  29 +
  30 + /**
  31 + * 按管道 ID 查找
  32 + *
  33 + * @param mapping 税务管道映射
  34 + * @return {@link List }<{@link TaxPipelineMapping }>
  35 + */
  36 + Page<TenantTaxPipelineMapping> findByPipelineIdAndState(TaxPipelineMapping mapping, IPage<?> page);
  37 +
  38 + /**
  39 + * 按管道 ID 和系统数据 ID 查找
  40 + *
  41 + * @param mapping 映射
  42 + * @return {@link Optional }<{@link TaxPipelineMapping }>
  43 + */
  44 + Optional<TenantTaxPipelineMapping> findByPipelineIdAndDocumentTypeAndSystemDataId(TaxPipelineMapping mapping);
  45 +
  46 + /**
  47 + * 创建租户映射表
  48 + *
  49 + * @param tableName 表名
  50 + */
  51 + void createTenantMappingTable(@Param("tableName") String tableName);
  52 +
  53 + /**
  54 + * 删除租户映射表
  55 + *
  56 + * @param tableName 表名
  57 + */
  58 + void dropTenantMappingTable(@Param("tableName") String tableName);
9 59 }
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/service/DynamicTaxPipelineMappingService.java 0 → 100644
  1 +package com.diligrp.tax.storage.service;
  2 +
  3 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4 +import com.diligrp.tax.central.exception.TaxAgentServiceException;
  5 +import com.diligrp.tax.central.model.TenantTaxPipelineMapping;
  6 +import com.diligrp.tax.central.service.ITaxPipelineMappingService;
  7 +import com.diligrp.tax.central.type.DocumentType;
  8 +import com.diligrp.tax.central.type.TaxSystemType;
  9 +import com.diligrp.tax.central.utils.JsonUtils;
  10 +import com.diligrp.tax.storage.domain.TaxPipelineMapping;
  11 +import com.diligrp.tax.storage.model.co.TaxPipelineMappingCO;
  12 +import com.diligrp.tax.storage.model.co.TaxPipelineMappingCreateCO;
  13 +import com.diligrp.tax.storage.repo.TaxPipelineMappingRepository;
  14 +import com.diligrp.tax.storage.type.MappingStateType;
  15 +import jakarta.annotation.Resource;
  16 +import org.springframework.stereotype.Component;
  17 +import org.springframework.transaction.annotation.Transactional;
  18 +import org.springframework.validation.annotation.Validated;
  19 +
  20 +import java.util.List;
  21 +import java.util.Optional;
  22 +
  23 +/**
  24 + * @Author: zhangmeiyang
  25 + * @CreateTime: 2025-11-07 10:41
  26 + * @Version: todo
  27 + */
  28 +@Component
  29 +public class DynamicTaxPipelineMappingService implements ITaxPipelineMappingService {
  30 +
  31 + @Resource
  32 + private TaxPipelineMappingRepository taxPipelineMappingRepository;
  33 +
  34 + private static final String DEFAULT_TAX_PIPELINE_MAPPING_TABLE_NAME = "tax_pipeline_mapping_";
  35 +
  36 + /**
  37 + * 创建租户映射表
  38 + *
  39 + * @param tenantId 租户 ID
  40 + */
  41 + @Transactional
  42 + public void createTenantMappingTable(String tenantId) {
  43 + String tenantIdValid = Optional.ofNullable(tenantId).orElseThrow(() -> new TaxAgentServiceException(TaxSystemType.MISSING_BUSINESS_INFORMATION, "租户ID不能为空"));
  44 + String tableName = DEFAULT_TAX_PIPELINE_MAPPING_TABLE_NAME + tenantIdValid;
  45 + taxPipelineMappingRepository.createTenantMappingTable(tableName);
  46 + }
  47 +
  48 + /**
  49 + * 查询租户管道数据
  50 + *
  51 + * @param taxPipelineMappingCO
  52 + * @return {@link List }<{@link TenantTaxPipelineMapping }>
  53 + */
  54 + public Page<TenantTaxPipelineMapping> findTenantPipelineData(TaxPipelineMappingCO taxPipelineMappingCO) {
  55 + Optional.ofNullable(taxPipelineMappingCO.getTenantId()).orElseThrow(() -> new TaxAgentServiceException(TaxSystemType.MISSING_BUSINESS_INFORMATION, "租户ID不能为空"));
  56 + Optional.ofNullable(taxPipelineMappingCO.getPipelineId()).orElseThrow(() -> new TaxAgentServiceException(TaxSystemType.MISSING_BUSINESS_INFORMATION, "管道ID不能为空"));
  57 + Page<TenantTaxPipelineMapping> page = new Page<>(taxPipelineMappingCO.getPageNumber(), taxPipelineMappingCO.getPageSize());
  58 + TaxPipelineMapping taxPipelineMapping = JsonUtils.convertValue(taxPipelineMappingCO, TaxPipelineMapping.class);
  59 + return taxPipelineMappingRepository.findByPipelineIdAndState(taxPipelineMapping, page);
  60 + }
  61 +
  62 + /**
  63 + * 按管道 ID 和文档类型以及系统数据 ID 查找
  64 + *
  65 + * @param pipelineId 管道 ID
  66 + * @param documentType 文档类型
  67 + * @param systemDataId 系统数据 ID
  68 + * @return {@link Optional }<{@link TenantTaxPipelineMapping }>
  69 + */
  70 + @Override
  71 + public Optional<TenantTaxPipelineMapping> findByPipelineIdAndDocumentTypeAndSystemDataId(Long tenantId, Long pipelineId, String documentType, String systemDataId) {
  72 + Optional.ofNullable(tenantId).orElseThrow(() -> new TaxAgentServiceException(TaxSystemType.MISSING_BUSINESS_INFORMATION, "租户ID不能为空"));
  73 + Optional.ofNullable(pipelineId).orElseThrow(() -> new TaxAgentServiceException(TaxSystemType.MISSING_BUSINESS_INFORMATION, "管道ID不能为空"));
  74 + Optional.ofNullable(documentType).orElseThrow(() -> new TaxAgentServiceException(TaxSystemType.MISSING_BUSINESS_INFORMATION, "文档类型不能为空"));
  75 + Optional.ofNullable(systemDataId).orElseThrow(() -> new TaxAgentServiceException(TaxSystemType.MISSING_BUSINESS_INFORMATION, "系统数据不能为空"));
  76 + TaxPipelineMapping taxPipelineMapping = new TaxPipelineMapping();
  77 + taxPipelineMapping.setTenantId(tenantId);
  78 + taxPipelineMapping.setPipelineId(pipelineId);
  79 + taxPipelineMapping.setDocumentType(documentType);
  80 + taxPipelineMapping.setSystemDataId(systemDataId);
  81 + taxPipelineMapping.setState(MappingStateType.SYNCED.value);
  82 + return taxPipelineMappingRepository.findByPipelineIdAndDocumentTypeAndSystemDataId(taxPipelineMapping);
  83 + }
  84 +
  85 + /**
  86 + * 插入
  87 + *
  88 + * @param taxPipelineMappingCO 税务管道测绘公司
  89 + */
  90 + public void insert(@Validated TaxPipelineMappingCreateCO taxPipelineMappingCO) {
  91 + DocumentType.validateDocumentType(taxPipelineMappingCO.getDocumentType());
  92 + TaxPipelineMapping taxPipelineMapping = JsonUtils.convertValue(taxPipelineMappingCO, TaxPipelineMapping.class);
  93 + taxPipelineMapping.setState(MappingStateType.SYNCED.value);
  94 + taxPipelineMappingRepository.insert(taxPipelineMapping);
  95 + }
  96 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/service/TaxPipelineConfigService.java
... ... @@ -63,7 +63,21 @@ public class TaxPipelineConfigService {
63 63 @Transactional
64 64 public void save(TaxPipelineConfigCO taxPipelineConfig) {
65 65 DocumentType.validateDocumentType(taxPipelineConfig.getDocumentType());
66   - taxPipelineConfigRepository.insertOrUpdate(JsonUtils.convertValue(taxPipelineConfig, TaxPipelineConfig.class));
  66 + TaxPipelineConfig config = JsonUtils.convertValue(taxPipelineConfig, TaxPipelineConfig.class);
  67 + taxPipelineConfigRepository.insert(config);
  68 + }
  69 +
  70 +
  71 + /**
  72 + * 更新
  73 + *
  74 + * @param taxPipelineConfig 税务管道配置
  75 + */
  76 + @Transactional
  77 + public void update(TaxPipelineConfigCO taxPipelineConfig) {
  78 + DocumentType.validateDocumentType(taxPipelineConfig.getDocumentType());
  79 + TaxPipelineConfig config = JsonUtils.convertValue(taxPipelineConfig, TaxPipelineConfig.class);
  80 + taxPipelineConfigRepository.updateById(config);
67 81 }
68 82  
69 83 /**
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/service/TaxPipelineMappingService.java 0 → 100644
  1 +package com.diligrp.tax.storage.service;
  2 +
  3 +/**
  4 + * @Author: zhangmeiyang
  5 + * @CreateTime: 2025-11-07 10:14
  6 + * @Version: todo
  7 + */
  8 +public class TaxPipelineMappingService {
  9 +}
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/service/TaxPipelineService.java
... ... @@ -44,15 +44,16 @@ public class TaxPipelineService {
44 44 SystemType.validateSystemCode(taxPipelineCO.getSystemCode());
45 45 TaxPipeline taxPipeline = JsonUtils.convertValue(taxPipelineCO, TaxPipeline.class);
46 46 taxPipeline.setState(StateType.ENABLE.value);
47   - taxPipelineRepository.insertOrUpdate(taxPipeline);
  47 + taxPipelineRepository.insert(taxPipeline);
48 48 }
49 49  
50 50  
51   - public List<TaxPipelineVO> listAllEnablePipeline(){
52   - LambdaQueryWrapper<TaxPipeline> queryWrapper = new LambdaQueryWrapper<>();
53   - queryWrapper.eq(TaxPipeline::getState, StateType.ENABLE.value);
54   - List<TaxPipeline> taxPipelines = taxPipelineRepository.selectList(queryWrapper);
55   - return taxPipelines.stream().map(taxPipeline -> JsonUtils.convertValue(taxPipeline, TaxPipelineVO.class)).toList();
  51 +
  52 + @Transactional
  53 + public void update(TaxPipelineCO taxPipelineCO) {
  54 + SystemType.validateSystemCode(taxPipelineCO.getSystemCode());
  55 + TaxPipeline taxPipeline = JsonUtils.convertValue(taxPipelineCO, TaxPipeline.class);
  56 + taxPipelineRepository.updateById(taxPipeline);
56 57 }
57 58  
58 59 /**
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/service/TaxTenantService.java
... ... @@ -27,6 +27,8 @@ public class TaxTenantService {
27 27  
28 28 @Resource
29 29 private TaxTenantRepository taxTenantRepository;
  30 + @Resource
  31 + private DynamicTaxPipelineMappingService dynamicTaxPipelineMappingService;
30 32  
31 33 /**
32 34 * 获取租户
... ... @@ -52,7 +54,19 @@ public class TaxTenantService {
52 54 public void saveTenant(TaxTenantCO taxTenantCO) {
53 55 TaxTenant taxTenant = JsonUtils.convertValue(taxTenantCO, TaxTenant.class);
54 56 taxTenant.setState(StateType.ENABLE.value);
55   - taxTenantRepository.insertOrUpdate(taxTenant);
  57 + taxTenantRepository.insert(taxTenant);
  58 + dynamicTaxPipelineMappingService.createTenantMappingTable(String.valueOf(taxTenant.getId()));
  59 + }
  60 +
  61 + /**
  62 + * 更新租户
  63 + *
  64 + * @param taxTenantCO 税务租户公司
  65 + */
  66 + @Transactional
  67 + public void updateTenant(TaxTenantCO taxTenantCO) {
  68 + TaxTenant taxTenant = JsonUtils.convertValue(taxTenantCO, TaxTenant.class);
  69 + taxTenantRepository.updateById(taxTenant);
56 70 }
57 71  
58 72 /**
... ...
tax-storage/src/main/java/com/diligrp/tax/storage/type/MappingStateType.java 0 → 100644
  1 +package com.diligrp.tax.storage.type;
  2 +
  3 +public enum MappingStateType {
  4 + SYNCED(0, "已同步"),
  5 + SYNC_FAILED(1, "同步失败"),
  6 + SYNC_RETRY(2, "同步重试");
  7 + public final int value;
  8 + public final String desc;
  9 +
  10 + MappingStateType(int value, String desc) {
  11 + this.value = value;
  12 + this.desc = desc;
  13 + }
  14 +}
... ...
tax-storage/src/main/resources/com/diligrp/tax/storage/repo/TaxPipelineMappingRepository.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="com.diligrp.tax.storage.repo.TaxPipelineMappingRepository">
  4 + <insert id="insert">
  5 + INSERT INTO
  6 + tax_pipeline_mapping_${tenantId}( pipeline_id
  7 + , document_type
  8 + , system_data_id
  9 + , pipeline_data_id
  10 + , origin_data
  11 + , state)
  12 + VALUES
  13 + ( #{pipelineId}
  14 + , #{documentType}
  15 + , #{systemDataId}
  16 + , #{pipelineDataId}
  17 + , #{originData}
  18 + , #{state})
  19 + </insert>
  20 + <update id="update">
  21 + UPDATE
  22 + tax_pipeline_mapping_${tenantId}
  23 + SET
  24 + state = #{state}
  25 + WHERE
  26 + id = #{id}
  27 + </update>
  28 + <select id="findByPipelineIdAndState" resultType="com.diligrp.tax.central.model.TenantTaxPipelineMapping">
  29 + SELECT
  30 + id
  31 + , pipeline_id
  32 + , document_type
  33 + , system_data_id
  34 + , pipeline_data_id
  35 + , origin_data
  36 + , state
  37 + , created_Time
  38 + , modified_Time
  39 + FROM
  40 + tax_pipeline_mapping_${tenantId}
  41 + WHERE
  42 + 1=1
  43 + AND pipeline_id = #{pipelineId}
  44 + <if test="state != null">
  45 + AND state = #{state}
  46 + </if>
  47 + </select>
  48 + <select id="findByPipelineIdAndDocumentTypeAndSystemDataId"
  49 + resultType="com.diligrp.tax.central.model.TenantTaxPipelineMapping">
  50 + SELECT
  51 + id
  52 + , pipeline_id
  53 + , document_type
  54 + , system_data_id
  55 + , pipeline_data_id
  56 + , origin_data
  57 + , state
  58 + , created_Time
  59 + , modified_Time
  60 + FROM
  61 + tax_pipeline_mapping_${tenantId}
  62 + WHERE
  63 + 1 = 1
  64 + AND pipeline_id = #{pipelineId}
  65 + AND document_type = #{documentType}
  66 + AND system_data_id = #{systemDataId}
  67 + </select>
  68 +
  69 + <!-- 建表语句 -->
  70 + <update id="createTenantMappingTable">
  71 + CREATE TABLE ${tableName}
  72 + (
  73 + `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  74 + `pipeline_id` bigint unsigned NOT NULL COMMENT '账套ID',
  75 + `document_type` varchar(40) NOT NULL COMMENT '业务单据类型',
  76 + `system_data_id` varchar(50) NOT NULL COMMENT '系统数据ID',
  77 + `pipeline_data_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '账套数据ID',
  78 + `origin_data` json NOT NULL COMMENT '原始数据',
  79 + `state` tinyint NOT NULL COMMENT '状态 1=同步成功,2=同步失败',
  80 + `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  81 + `modified_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  82 + PRIMARY KEY (`id`) USING BTREE,
  83 + UNIQUE KEY `uk_pipeline_id_document_type_system_data_id` (`pipeline_id`, `document_type`, `system_data_id`) USING BTREE,
  84 + KEY `idx_pipeline_id_state` (`pipeline_id`, `state`) USING BTREE
  85 + ) ENGINE = InnoDB
  86 + DEFAULT CHARSET = utf8mb4
  87 + COLLATE = utf8mb4_0900_ai_ci COMMENT ='账套同步数据';
  88 + </update>
  89 +
  90 + <!-- 删除表语句 -->
  91 + <update id="dropTenantMappingTable">
  92 + DROP TABLE IF EXISTS ${tableName}
  93 + </update>
  94 +
  95 +</mapper>
... ...