diff --git a/发版/2025/20250613-派送翼对接发布.md b/发版/2025/20250613-派送翼对接发布.md new file mode 100644 index 0000000..b48715d --- /dev/null +++ b/发版/2025/20250613-派送翼对接发布.md @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + +# 电商订单对接发布 + +## 发布说明 + +### 涉及项目 + +| 项目 | 分支 | 发布顺序 | 数据库变更 | 配置变更 | 负责人 | 备注 | +|-----------------|---------------------------|------|-----------------------|-------------------------|-----|----------------------------| +| yuanmeng-engine | feat/20250520-starter-mybatis-plus | 1 | | | 刘晓华 | 升级 2.0.0 | +| yuanmeng-parent | feat/20250520-starter-mybatis-plus | 2 | | | 刘晓华 | 升级 2.0.0 | +| qifu-saas-eg | master | 3 | [数据库脚本DDL](#数据库脚本DDL) | [Nacos配置文件](#Nacos配置文件) | 刘晓华 | 注意配置文件中需要修改的部分需要修改为对应环境的数据 | + +### 附件 + +#### 数据库脚本DDL + +##### qifu-saas-eg数据库DDL + +```sql +CREATE TABLE `eg_express_agent_shipping` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `channel` varchar(20) NOT NULL COMMENT '渠道编码', + `company` varchar(50) NOT NULL COMMENT '公司编码', + `biz_type` int(4) NOT NULL DEFAULT '1' COMMENT '业务类型:1=出库单', + `biz_code` varchar(50) NOT NULL DEFAULT '' COMMENT '业务编码', + `order_no` varchar(50) NOT NULL DEFAULT '' COMMENT 'Keyfil平台单号', + `status` int(4) NOT NULL DEFAULT '1' COMMENT '下单状态:1=待下单,2=已下单,3=已确认,4=已扫描,5=已取消', + `receiver` text COMMENT '收件人信息', + `sender` text COMMENT '发件人信息', + `package_list` text COMMENT '包裹信息列表', + `attach_info` text COMMENT '附加信息', + `out_order_no` varchar(50) NOT NULL DEFAULT '' COMMENT '外部一键代发单号', + `tracking_no` varchar(50) NOT NULL DEFAULT '' COMMENT '外部追踪号', + `child_tracking_no_list` text COMMENT '如果多包裹下单,子包裹的跟踪号列表', + `main_track_number_list` text COMMENT '如果拆单,主单号集合列表', + `ups_tracking_number` varchar(50) DEFAULT NULL COMMENT 'ups tracking number', + `label_list` text COMMENT '标签列表', + `pdf` varchar(255) DEFAULT NULL COMMENT 'pdf', + `create_user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '创建人ID', + `create_by` varchar(32) NOT NULL DEFAULT '' COMMENT '创建人名称', + `create_time` bigint(20) NOT NULL COMMENT '创建时间', + `update_user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '更新人id', + `update_by` varchar(32) NOT NULL DEFAULT '' COMMENT '更新人名称', + `update_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '更新时间', + `deleted` bigint(20) NOT NULL DEFAULT '0' COMMENT '是否删除:0=否,id=是', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='一键代发下单表'; + +ALTER TABLE `eg_order_app_seller` + ADD COLUMN `state` int(2) NOT NULL DEFAULT 1 COMMENT '状态:1=启用,2=禁用' AFTER `refresh_token_expires_time`; +``` + +##### qifu-saas-eg数据库DML + +- 测试 + +```sql + +``` + +#### Nacos配置文件 (开发测试) + +```yaml +yuanmeng: + mybatis-plus: + interceptor: + tenant-line: + include-mode: true +``` + +### 其余配置 + +#### 开发者平台配置 + + +---- + +## 开发说明 + +### 功能列表 + diff --git a/文档/基础组件/feign/20250605-engine-starter-feign-2.0.1.md b/文档/基础组件/feign/20250605-engine-starter-feign-2.0.1.md new file mode 100644 index 0000000..303693f --- /dev/null +++ b/文档/基础组件/feign/20250605-engine-starter-feign-2.0.1.md @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + +# engine-starter-feign 使用教程 + +> 基于 Feign 的通用封装 +> 具体使用可参考 [engine-sample > engine-sample-starter-web](../engine-sample/engine-sample-starter-web) + +## 功能特性 + +- [X] 请求头添加内部请求标识 `X-QiFu-From-In: true` +- [X] 请求头透传 +- [X] 请求结果解析映射(解封装 `Result`) +- [X] 添加请求头透传 + +------- + +## 快速使用 + +- **注意:** `qifu-saas-parent >= 2.0.1-SNAPSHOT` +- **注意:** 启动类 `@ComponentScan` 需要变更为如下形式 +- ```java + @ComponentScan(value = "com.yuanmeng.*", + excludeFilters = { + @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), + @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) + }) + @SpringBootApplication + public class SampleStarterWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleStarterWebApplication.class, args); + } + + } + ``` + +### 添加依赖 + +```xml + + + com.yuanmeng.engine + engine-starter-feign + +``` + +### 添加兼容包(可选) + +- 项目中存在 `web-core` 时需要添加兼容包 + +```xml + + + com.yuanmeng.engine + engine-starter-compatible-feign + +``` + +------ + +## 完整默认配置文件 + +```yaml +yuanmeng: + feign: + #- 开启feign自动配置 + enable: true + #- 开启请求头内部标识 + enable-contract: true + #- 开启旧包兼容 + enable-ignore: true + #- 开启结果解析 + enable-decoder: true + #- 开启请求头传递 + enable-interceptor: true + #- 链接超时时间 + connect-timeout: 10 + connect-time-unit: seconds + #- 读取超时时间 + read-timeout: 60 + read-time-unit: seconds +``` diff --git a/文档/基础组件/web/20250605-engine-starter-web-2.0.1.md b/文档/基础组件/web/20250605-engine-starter-web-2.0.1.md new file mode 100644 index 0000000..177acf9 --- /dev/null +++ b/文档/基础组件/web/20250605-engine-starter-web-2.0.1.md @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + +# engine-starter-web 使用教程 + +> 基于 web 的各种工具封装 +> 具体使用可参考 [engine-sample > engine-sample-starter-web](../engine-sample/engine-sample-starter-web) + +## 功能特性 + +- [X] `@YmAutoFill` 和 `@YmAutoFillResp`: 结果自动填充 +- [X] `@YmIgnoreResultWrapper`: 忽略 `Result` 结果包装 +- [X] `YmResponseAdvice`: 全局结果封装 +- [X] `YmGlobalExceptionHandlerAdvice`: 全局异常拦截处理 +- [X] `YmAutofillResponseAdvice`: 全局返回结果填充处理 +- [X] `YmI18nResponseAdvice`: I18n全局处理 +- [X] `@YmIgnoreI18n`: 忽略I18n +- [X] `YmHealthCheckFilter`: 全局健康检查接口 +- [X] `YmRequestBodyWrapperFilter`: request 和 response 可重复读封装 +- [X] `YmRequestPrintFilter`: 请求日志打印 +- [X] `YmThreadLocalFilter`: 登录用户信息解析存储(兼容新网关) +- [X] `YmUserLoginFilter`: 用户登录校验(后续会迁移到网关,默认关闭) +- [X] `YmThreadLocalUtils`: 获取当前线程中的上下文信息 + +------- + +## 快速使用 + +- **注意:** `qifu-saas-parent >= 2.0.1-SNAPSHOT` +- **注意:** 启动类 `@ComponentScan` 需要变更为如下形式 +- ```java + @ComponentScan(value = "com.yuanmeng.*", + excludeFilters = { + @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), + @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) + }) + @SpringBootApplication + public class SampleStarterWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleStarterWebApplication.class, args); + } + + } + ``` + +### 添加依赖 + +```xml + + + com.yuanmeng.engine + engine-starter-web + +``` + +### 添加兼容包(可选) + +- 项目中存在 `web-core` 或者 `oauth2-core` 时需要添加兼容包 + +```xml + + + com.yuanmeng.engine + engine-starter-compatible-web + +``` + +---- + +## 使用工具 + +### 忽略结果封装 + +```java +public class Usage { + + /** + * 测试忽略包装 + * + * @return 结果 + */ + @YmIgnoreResultWrapper + @GetMapping("/ignore-result-wrapper") + public String ignoreResultWrapper() { + return "ignore-result-wrapper success"; + } + +} +``` + +### 全局异常拦截 + +```java +public class Usage { + + @GetMapping("/exception") + public String exception() { + // 打印error日志,触发告警 + throw new RuntimeException("exception"); + } + + @GetMapping("/ym-exception") + public String ymException() { + // 打印error日志,触发告警 + throw new YmException("ym exception"); + } + + @GetMapping("/ym-biz-exception") + public String ymBizException() { + // 打印 warning 日志 + throw new YmBizException(YmResultCode.BASE_ERROR); + } + + @GetMapping("/ym-biz-data-exception") + public String ymBizDataException() { + // 打印 info 日志 + throw new YmBizDataException(YmResultCode.BASE_ERROR, + new TestData(TestStatusEnum.SUCCESS.getCode(), "1")); + } + + @YmAutofillResp + @GetMapping("/ym-biz-data-exception-fill") + public String ymBizDataExceptionFill() { + // 触发结果数据自动填充 + throw new YmBizDataException(YmResultCode.BASE_ERROR, + new TestData(TestStatusEnum.SUCCESS.getCode(), "1")); + } + +} +``` + +### 国际化支持 + +- 开启国际化支撑 + +```yaml +yuanmeng: + web: + i18n: + enable: true +``` + +- 添加国际化文件 + - 默认 `resources > i18n > messages_zh_CN.properties` + +```properties +Base\ Error=服务端错误 +``` + +- 编写代码 + +```java + +@GetMapping("/i18n") +public String i18n() { + return "test success"; +} + +@YmIgnoreI18n +@GetMapping("/i18n-ignore") +public String i18nIgnore() { + return "i18n ignore"; +} + +@GetMapping("/i18n-error") +public String i18nError() { + // 触发国际化配置 + throw new YmBizException(YmResultCode.BASE_ERROR); +} +``` + +- **自定义国际化处理类(可选)** + +```java + +@RequiredArgsConstructor +public class CustomI18nMessageHandler implements YmI18nMessageHandler { + + private final MessageSource messageSource; + + @Override + public String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) { + return messageSource.getMessage(code, args, defaultMessage, locale); + } + + @Override + public String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException { + return messageSource.getMessage(code, args, locale); + } + + @Override + public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException { + return messageSource.getMessage(resolvable, locale); + } + +} + +@Configuration +public class YmI18nConfiguration { + + public static final String I18N_MESSAGE_SOURCE = "ymI18nMessageSource"; + public static final String I18N_MESSAGE_HANDLER = "ymI18nMessageHandler"; + + @Bean(I18N_MESSAGE_SOURCE) + @ConditionalOnMissingBean(name = I18N_MESSAGE_SOURCE) + public MessageSource messageSource() { + log.info("[WEB_STARTER_INIT]: CUSTOM I18N_MESSAGE_SOURCE init"); + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + messageSource.setBasename("i18n/test"); + messageSource.setDefaultEncoding(StandardCharsets.UTF_8.toString()); + return messageSource; + } + + @Bean(I18N_MESSAGE_HANDLER) + @ConditionalOnMissingBean(name = I18N_MESSAGE_HANDLER) + public YmI18nMessageHandler ymI18nMessageHandler(@Qualifier(I18N_MESSAGE_SOURCE) MessageSource messageSource) { + log.info("[WEB_STARTER_INIT]: CUSTOM YmI18nMessageHandler init"); + return new CustomI18nMessageHandler(messageSource); + } + +} +``` + +### 结果自动填充 + +```java + +@Getter +@AllArgsConstructor +public enum TestStatusEnum implements YmBaseEnum { + + SUCCESS("SUCCESS", "成功"), + FAIL("FAIL", "失败"), + ; + + private final String code; + private final String desc; + +} + +@Component +public class TestAutoFillHandler implements YmAutofillHandler { + + @Override + public Map handle(Collection ids, YmAutofillInfo autofill) { + Collection string = (Collection) ids; + Map result = new HashMap<>(); + string.forEach(k -> result.put(k, k + "value")); + return result; + } + +} + +@Data +@NoArgsConstructor +public class TestData { + + @YmAutofill(enumCls = TestStatusEnum.class) + private String statusName; + private String status; + + private String code; + @YmAutofill(from = "code", type = YmAutofillType.BEAN, handler = TestAutoFillHandler.class) + private String msg; + + public TestData(String status, String code) { + this.status = status; + this.code = code; + } + +} + +@Data +@NoArgsConstructor +public class TestDataSecond { + + private String statusName; + private String status; + + private String code; + private String msg; + + public TestDataSecond(String status, String code) { + this.status = status; + this.code = code; + } + +} + + +public class Usage { + + @YmAutofillResp + @GetMapping("/autofill") + public List autofill() { + List list = new ArrayList<>(); + list.add(new TestData(TestStatusEnum.SUCCESS.getCode(), "1")); + list.add(new TestData(TestStatusEnum.FAIL.getCode(), "2")); + return list; + } + + @YmAutofillResp({ + @YmAutofill(from = "status", target = "statusName", enumCls = TestStatusEnum.class), + @YmAutofill(from = "code", target = "msg", type = YmAutofillType.BEAN, handler = TestAutoFillHandler.class) + }) + @GetMapping("/autofill-second") + public List autofillSecond() { + List list = new ArrayList<>(); + list.add(new TestDataSecond(TestStatusEnum.SUCCESS.getCode(), "1")); + list.add(new TestDataSecond(TestStatusEnum.FAIL.getCode(), "2")); + return list; + } + +} +``` + +------ + +## 完整默认配置文件 + +```yaml +yuanmeng: + web: + #- 开启starter + enable: true + #- 开启兼容排除 + enable-ignore: true + #- 开启返回结果填充 + enable-audit-handler: true + #- 开启返回结果自动封装 YmResult + enable-result-wrapper: true + #- 开启全局异常拦截 + enable-exception-handler: true + filter: + #- 开启健康检查接口 + enable-health-check: true + #- 开启上下文信息解析 + enable-thread-local: true + #- 开启登录拦截器(token校验) + enable-user-login: false + #- 需要跳过的路径 + skip-path: + - "/test" + #- 可重复读请求封装 + request-body: + enable: true + filter-uris: + - "/test1" + #- 请求日志打印封装 + track: + enable: true + filter-uris: + - "/test2" + sensitive-keys: + - "password" + print-type: 2 + #- 打印请求体 + print-request-body: true + #- 打印返回体 + print-response-body: true + external-url-list: + - "/external/test" + #- i18n全局拦截 + i18n: + enable: false +``` \ No newline at end of file