Forráskód Böngészése

Merge remote-tracking branch 'origin/sit' into sit

fcw 1 hete
szülő
commit
e7ef6eba2b

+ 63 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeUserThirdBind.java

@@ -0,0 +1,63 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@JsonInclude
+@TableName("life_user_third_bind")
+@ApiModel("LifeUserThirdBind")
+public class LifeUserThirdBind {
+
+    public static final String PLATFORM_WECHAT = "WECHAT";
+    public static final String PLATFORM_ALIPAY = "ALIPAY";
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @TableField("life_user_id")
+    @ApiModelProperty("life_user.id")
+    private Integer lifeUserId;
+
+    @TableField("platform")
+    @ApiModelProperty("WECHAT / ALIPAY")
+    private String platform;
+
+    @TableField("openid")
+    private String openid;
+
+    @TableField("unionid")
+    private String unionid;
+
+    @TableField("alipay_user_id")
+    private String alipayUserId;
+
+    @TableField("account_label")
+    @ApiModelProperty("昵称/展示名")
+    private String accountLabel;
+
+    @TableField("bind_phone")
+    @ApiModelProperty("渠道授权手机号")
+    private String bindPhone;
+
+    @TableField("remark")
+    private String remark;
+
+    @TableLogic
+    @TableField("delete_flag")
+    private Integer deleteFlag;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}

+ 57 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LifeUserThirdBindInfoVo.java

@@ -0,0 +1,57 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 当前用户微信/支付宝绑定信息:昵称、平台账号标识、渠道手机号等
+ */
+@Data
+@ApiModel("用户第三方绑定查询")
+public class LifeUserThirdBindInfoVo {
+
+    @ApiModelProperty("life_user.id")
+    private Integer lifeUserId;
+
+    @ApiModelProperty("用户账号手机号(life_user.user_phone)")
+    private String lifeUserPhone;
+
+    @ApiModelProperty("微信")
+    private WechatBindItem wechat;
+
+    @ApiModelProperty("支付宝")
+    private AlipayBindItem alipay;
+
+    @Data
+    @ApiModel("微信绑定项")
+    public static class WechatBindItem {
+        @ApiModelProperty("是否已绑定")
+        private Boolean bound;
+        @ApiModelProperty("微信昵称(与 accountLabel 同源,便于前端语义)")
+        private String nickName;
+        @ApiModelProperty("展示名(数据库 account_label)")
+        private String accountLabel;
+        @ApiModelProperty("平台账号标识:openid")
+        private String openid;
+        @ApiModelProperty("unionid")
+        private String unionid;
+        @ApiModelProperty("微信侧授权手机号")
+        private String bindPhone;
+    }
+
+    @Data
+    @ApiModel("支付宝绑定项")
+    public static class AlipayBindItem {
+        @ApiModelProperty("是否已绑定")
+        private Boolean bound;
+        @ApiModelProperty("支付宝昵称/展示名")
+        private String nickName;
+        @ApiModelProperty("展示名(数据库 account_label)")
+        private String accountLabel;
+        @ApiModelProperty("平台账号标识:支付宝 user_id")
+        private String alipayUserId;
+        @ApiModelProperty("支付宝侧授权手机号")
+        private String bindPhone;
+    }
+}

+ 9 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeUserThirdBindMapper.java

@@ -0,0 +1,9 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.LifeUserThirdBind;
+
+@Mapper
+public interface LifeUserThirdBindMapper extends BaseMapper<LifeUserThirdBind> {
+}

+ 22 - 0
alien-gateway/src/main/java/shop/alien/gateway/config/LifeUserThirdBindProperties.java

@@ -0,0 +1,22 @@
+package shop.alien.gateway.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "life.user.third-bind")
+public class LifeUserThirdBindProperties {
+
+    private boolean enabled = true;
+
+    private Wechat wechat = new Wechat();
+
+    @Data
+    public static class Wechat {
+        /** 微信开放平台「移动应用」AppID / AppSecret(App 内绑定) */
+        private String mobileAppId;
+        private String mobileAppSecret;
+    }
+}

+ 96 - 0
alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserThirdBindController.java

@@ -0,0 +1,96 @@
+package shop.alien.gateway.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LifeUserThirdBindInfoVo;
+import shop.alien.gateway.dto.LifeUserThirdBindWechatRequest;
+import shop.alien.gateway.service.LifeUserThirdBindService;
+import shop.alien.util.common.JwtUtil;
+
+import javax.validation.Valid;
+
+/**
+ * 用户第三方账号绑定(当前仅开放微信)
+ */
+@Api(tags = {"一期-用户-第三方绑定"})
+@Slf4j
+@CrossOrigin
+@RestController
+@RequestMapping("/user/third-bind")
+@RequiredArgsConstructor
+public class LifeUserThirdBindController {
+
+    private final LifeUserThirdBindService lifeUserThirdBindService;
+
+    @ApiOperation("查询当前用户微信绑定(昵称、openid、渠道手机号等)")
+    @ApiOperationSupport(order = 1)
+    @GetMapping("/info")
+    public R<LifeUserThirdBindInfoVo> thirdBindInfo() {
+        try {
+            JSONObject sub = JwtUtil.getCurrentUserInfo();
+            if (sub == null) {
+                return R.fail("请登录");
+            }
+            Integer userId = lifeUserThirdBindService.resolveLifeUserId(sub);
+            if (userId == null) {
+                return R.fail("仅支持用户端查询");
+            }
+            return R.data(lifeUserThirdBindService.getThirdBindInfo(userId));
+        } catch (Exception e) {
+            log.warn("thirdBindInfo token parse failed", e);
+            return R.fail("登录已失效");
+        }
+    }
+
+    @ApiOperation("绑定微信(App 移动应用授权 code)")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/wechat")
+    public R<LifeUserThirdBindInfoVo> thirdBindWechat(@Valid @RequestBody LifeUserThirdBindWechatRequest body) {
+        try {
+            JSONObject sub = JwtUtil.getCurrentUserInfo();
+            if (sub == null) {
+                return R.fail("请登录");
+            }
+            Integer userId = lifeUserThirdBindService.resolveLifeUserId(sub);
+            if (userId == null) {
+                return R.fail("仅支持用户端操作");
+            }
+            return lifeUserThirdBindService.bindWechat(
+                    userId, body.getCode(), body.getNickName(), body.getBindPhone());
+        } catch (Exception e) {
+            log.warn("thirdBindWechat failed", e);
+            return R.fail("登录已失效");
+        }
+    }
+
+    @ApiOperation("解绑微信")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/wechat/unbind")
+    public R<LifeUserThirdBindInfoVo> thirdBindWechatUnbind() {
+        try {
+            JSONObject sub = JwtUtil.getCurrentUserInfo();
+            if (sub == null) {
+                return R.fail("请登录");
+            }
+            Integer userId = lifeUserThirdBindService.resolveLifeUserId(sub);
+            if (userId == null) {
+                return R.fail("仅支持用户端操作");
+            }
+            return lifeUserThirdBindService.unbindWechat(userId);
+        } catch (Exception e) {
+            log.warn("thirdBindWechatUnbind failed", e);
+            return R.fail("登录已失效");
+        }
+    }
+}

+ 22 - 0
alien-gateway/src/main/java/shop/alien/gateway/dto/LifeUserThirdBindWechatRequest.java

@@ -0,0 +1,22 @@
+package shop.alien.gateway.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel("绑定微信请求")
+public class LifeUserThirdBindWechatRequest {
+
+    @NotBlank(message = "code 不能为空")
+    @ApiModelProperty(value = "微信开放平台移动应用 SDK 授权返回的 code", required = true)
+    private String code;
+
+    @ApiModelProperty("微信昵称(用户授权后由 App 取到并传入;换票接口不含昵称)")
+    private String nickName;
+
+    @ApiModelProperty("微信侧手机号(如开放平台/组件授权后由客户端传入,可选)")
+    private String bindPhone;
+}

+ 207 - 0
alien-gateway/src/main/java/shop/alien/gateway/service/LifeUserThirdBindService.java

@@ -0,0 +1,207 @@
+package shop.alien.gateway.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.LifeUserThirdBind;
+import shop.alien.entity.store.vo.LifeUserThirdBindInfoVo;
+import shop.alien.gateway.config.LifeUserThirdBindProperties;
+import shop.alien.gateway.mapper.LifeUserGatewayMapper;
+import shop.alien.gateway.thirdparty.WeChatMobileOAuthClient;
+import shop.alien.mapper.LifeUserThirdBindMapper;
+
+import java.util.Date;
+
+@Service
+@RequiredArgsConstructor
+public class LifeUserThirdBindService {
+
+    private final LifeUserGatewayMapper lifeUserMapper;
+    private final LifeUserThirdBindMapper thirdBindMapper;
+    private final LifeUserThirdBindProperties thirdBindProperties;
+    private final WeChatMobileOAuthClient weChatMobileOAuthClient;
+
+    public Integer resolveLifeUserId(JSONObject sub) {
+        if (sub == null) {
+            return null;
+        }
+        String userType = sub.getString("userType");
+        if (!"user".equals(userType) && !"miniprogram_user".equals(userType)) {
+            return null;
+        }
+        String userIdStr = sub.getString("userId");
+        if (StringUtils.isNotBlank(userIdStr)) {
+            try {
+                return Integer.parseInt(userIdStr);
+            } catch (NumberFormatException ignored) {
+                // fall through
+            }
+        }
+        String phone = sub.getString("phone");
+        if (StringUtils.isNotBlank(phone)) {
+            LifeUser u = lifeUserMapper.selectOne(
+                    new LambdaQueryWrapper<LifeUser>().eq(LifeUser::getUserPhone, phone).last("LIMIT 1"));
+            return u != null ? u.getId() : null;
+        }
+        return null;
+    }
+
+    public LifeUserThirdBindInfoVo getThirdBindInfo(Integer lifeUserId) {
+        LifeUserThirdBindInfoVo vo = new LifeUserThirdBindInfoVo();
+        vo.setLifeUserId(lifeUserId);
+        LifeUser user = lifeUserMapper.selectById(lifeUserId);
+        if (user != null) {
+            vo.setLifeUserPhone(user.getUserPhone());
+        }
+        LifeUserThirdBind wechat = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_WECHAT);
+        LifeUserThirdBind alipay = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_ALIPAY);
+        vo.setWechat(toWechat(wechat));
+        vo.setAlipay(toAlipay(alipay));
+        return vo;
+    }
+
+    private LifeUserThirdBind latestRow(Integer lifeUserId, String platform) {
+        return thirdBindMapper.selectOne(
+                new LambdaQueryWrapper<LifeUserThirdBind>()
+                        .eq(LifeUserThirdBind::getLifeUserId, lifeUserId)
+                        .eq(LifeUserThirdBind::getPlatform, platform)
+                        .orderByDesc(LifeUserThirdBind::getUpdatedTime)
+                        .last("LIMIT 1"));
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public R<LifeUserThirdBindInfoVo> bindWechat(Integer lifeUserId, String code,
+                                                 String nickName, String bindPhone) {
+        if (!thirdBindProperties.isEnabled()) {
+            return R.fail("第三方绑定功能已关闭");
+        }
+        String guard = assertUserBindableOrMsg(lifeUserId);
+        if (guard != null) {
+            return R.fail(guard);
+        }
+        WeChatMobileOAuthClient.OAuthExchangeResult session = weChatMobileOAuthClient.exchangeCode(code);
+        if (!session.isSuccess()) {
+            String msg = session.getErrmsg();
+            if (session.getErrcode() != null) {
+                msg = StringUtils.defaultString(msg) + "(errcode=" + session.getErrcode() + ")";
+            }
+            return R.fail(StringUtils.defaultIfBlank(msg, "微信授权失败"));
+        }
+        String openid = session.getOpenid();
+        if (isWechatOpenidBoundToOtherUser(openid, lifeUserId)) {
+            return R.fail("该微信已绑定其他账号");
+        }
+        String label = StringUtils.trimToNull(nickName);
+        String phone = StringUtils.trimToNull(bindPhone);
+        upsertWechatRow(lifeUserId, openid, session.getUnionid(), label, phone);
+        return R.data(getThirdBindInfo(lifeUserId));
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public R<LifeUserThirdBindInfoVo> unbindWechat(Integer lifeUserId) {
+        if (!thirdBindProperties.isEnabled()) {
+            return R.fail("第三方绑定功能已关闭");
+        }
+        String guard = assertUserBindableOrMsg(lifeUserId);
+        if (guard != null) {
+            return R.fail(guard);
+        }
+        LifeUserThirdBind row = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_WECHAT);
+        if (row == null) {
+            return R.fail("当前未绑定微信");
+        }
+        thirdBindMapper.deleteById(row.getId());
+        return R.data(getThirdBindInfo(lifeUserId));
+    }
+
+    private String assertUserBindableOrMsg(Integer lifeUserId) {
+        LifeUser user = lifeUserMapper.selectById(lifeUserId);
+        if (user == null) {
+            return "用户不存在";
+        }
+        if (user.getLogoutFlag() != null && user.getLogoutFlag() == 1) {
+            return "账号已注销,无法操作绑定";
+        }
+        if (user.getIsBanned() != null && user.getIsBanned() == 1) {
+            return "账号已被封禁,无法操作绑定";
+        }
+        return null;
+    }
+
+    private boolean isWechatOpenidBoundToOtherUser(String openid, Integer lifeUserId) {
+        if (StringUtils.isBlank(openid)) {
+            return false;
+        }
+        long n = thirdBindMapper.selectCount(
+                new LambdaQueryWrapper<LifeUserThirdBind>()
+                        .eq(LifeUserThirdBind::getPlatform, LifeUserThirdBind.PLATFORM_WECHAT)
+                        .eq(LifeUserThirdBind::getOpenid, openid)
+                        .ne(LifeUserThirdBind::getLifeUserId, lifeUserId));
+        return n > 0;
+    }
+
+    private void upsertWechatRow(Integer lifeUserId, String openid, String unionid, String accountLabel, String bindPhone) {
+        Date now = new Date();
+        LifeUserThirdBind existing = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_WECHAT);
+        if (existing != null) {
+            existing.setOpenid(openid);
+            existing.setUnionid(unionid);
+            if (accountLabel != null) {
+                existing.setAccountLabel(accountLabel);
+            }
+            if (bindPhone != null) {
+                existing.setBindPhone(bindPhone);
+            }
+            existing.setUpdatedTime(now);
+            thirdBindMapper.updateById(existing);
+        } else {
+            LifeUserThirdBind row = new LifeUserThirdBind();
+            row.setLifeUserId(lifeUserId);
+            row.setPlatform(LifeUserThirdBind.PLATFORM_WECHAT);
+            row.setOpenid(openid);
+            row.setUnionid(unionid);
+            row.setAccountLabel(accountLabel);
+            row.setBindPhone(bindPhone);
+            row.setDeleteFlag(0);
+            row.setCreatedTime(now);
+            row.setUpdatedTime(now);
+            thirdBindMapper.insert(row);
+        }
+    }
+
+    private static LifeUserThirdBindInfoVo.WechatBindItem toWechat(LifeUserThirdBind row) {
+        LifeUserThirdBindInfoVo.WechatBindItem it = new LifeUserThirdBindInfoVo.WechatBindItem();
+        if (row == null) {
+            it.setBound(false);
+            return it;
+        }
+        it.setBound(true);
+        String label = row.getAccountLabel();
+        it.setNickName(label);
+        it.setAccountLabel(label);
+        it.setOpenid(row.getOpenid());
+        it.setUnionid(row.getUnionid());
+        it.setBindPhone(row.getBindPhone());
+        return it;
+    }
+
+    private static LifeUserThirdBindInfoVo.AlipayBindItem toAlipay(LifeUserThirdBind row) {
+        LifeUserThirdBindInfoVo.AlipayBindItem it = new LifeUserThirdBindInfoVo.AlipayBindItem();
+        if (row == null) {
+            it.setBound(false);
+            return it;
+        }
+        it.setBound(true);
+        String label = row.getAccountLabel();
+        it.setNickName(label);
+        it.setAccountLabel(label);
+        it.setAlipayUserId(row.getAlipayUserId());
+        it.setBindPhone(row.getBindPhone());
+        return it;
+    }
+}

+ 100 - 0
alien-gateway/src/main/java/shop/alien/gateway/thirdparty/WeChatMobileOAuthClient.java

@@ -0,0 +1,100 @@
+package shop.alien.gateway.thirdparty;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.util.UriComponentsBuilder;
+import shop.alien.gateway.config.LifeUserThirdBindProperties;
+
+import java.time.Duration;
+
+/**
+ * 微信开放平台「移动应用」OAuth2:App 内 SDK 授权 code 换 openid/unionid。
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class WeChatMobileOAuthClient {
+
+    private static final String OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
+
+    private final LifeUserThirdBindProperties properties;
+
+    @Value("${wechat.open.mobileAppId:}")
+    private String wechatMobileAppIdFallback;
+
+    @Value("${wechat.open.mobileAppSecret:}")
+    private String wechatMobileAppSecretFallback;
+
+    private final WebClient webClient = WebClient.builder().build();
+
+    public OAuthExchangeResult exchangeCode(String code) {
+        OAuthExchangeResult result = new OAuthExchangeResult();
+        String appId = StringUtils.firstNonBlank(
+                properties.getWechat().getMobileAppId(),
+                wechatMobileAppIdFallback);
+        String secret = StringUtils.firstNonBlank(
+                properties.getWechat().getMobileAppSecret(),
+                wechatMobileAppSecretFallback);
+        if (StringUtils.isBlank(appId) || StringUtils.isBlank(secret)) {
+            result.setErrmsg("未配置微信移动应用 appId/appSecret(life.user.third-bind.wechat.mobile-app-id 或 wechat.open.mobileAppId)");
+            return result;
+        }
+        if (StringUtils.isBlank(code)) {
+            result.setErrmsg("code 不能为空");
+            return result;
+        }
+        String url = UriComponentsBuilder.fromHttpUrl(OAUTH2_ACCESS_TOKEN_URL)
+                .queryParam("appid", appId)
+                .queryParam("secret", secret)
+                .queryParam("code", code)
+                .queryParam("grant_type", "authorization_code")
+                .build(true)
+                .toUriString();
+        try {
+            String body = webClient.get()
+                    .uri(url)
+                    .retrieve()
+                    .bodyToMono(String.class)
+                    .block(Duration.ofSeconds(15));
+            log.info("微信 App oauth2/access_token 已返回, bodyLength={}", body != null ? body.length() : 0);
+            if (StringUtils.isBlank(body)) {
+                result.setErrmsg("微信接口无响应");
+                return result;
+            }
+            JSONObject json = JSONObject.parseObject(body);
+            if (json.containsKey("errcode") && json.getIntValue("errcode") != 0) {
+                result.setErrcode(json.getInteger("errcode"));
+                result.setErrmsg(json.getString("errmsg"));
+                return result;
+            }
+            result.setOpenid(json.getString("openid"));
+            result.setUnionid(json.getString("unionid"));
+            if (StringUtils.isBlank(result.getOpenid())) {
+                result.setErrmsg("未返回 openid");
+            }
+            return result;
+        } catch (Exception e) {
+            log.error("调用微信 App oauth2/access_token 异常", e);
+            result.setErrmsg("调用微信接口失败");
+            return result;
+        }
+    }
+
+    @Data
+    public static class OAuthExchangeResult {
+        private String openid;
+        private String unionid;
+        private Integer errcode;
+        private String errmsg;
+
+        public boolean isSuccess() {
+            return errcode == null && StringUtils.isNotBlank(openid);
+        }
+    }
+}

+ 6 - 0
alien-job/pom.xml

@@ -137,6 +137,12 @@
             <groupId>org.freemarker</groupId>
             <artifactId>freemarker</artifactId>
         </dependency>
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-store</artifactId>
+            <version>1.0.0</version>
+            <scope>compile</scope>
+        </dependency>
         <!-- mybatis-plus代码生成器 End -->
 
     </dependencies>

+ 21 - 0
alien-store/src/main/java/shop/alien/store/feign/DiningServiceFeign.java

@@ -271,6 +271,27 @@ public interface DiningServiceFeign {
     @PostMapping(value = "/dining/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
     R<String> uploadFile(@RequestPart("file") MultipartFile file);
 
+    @GetMapping("/payment/prePay")
+    R<Object> prePay(
+            @RequestHeader(value = "Authorization", required = false) String authorization,
+            @RequestParam("price") String price,
+            @RequestParam("subject") String subject,
+            @RequestParam("payType") String payType,
+            @RequestParam("payer") String payer,
+            @RequestParam("orderNo") String orderNo,
+            @RequestParam("storeId") Integer storeId,
+            @RequestParam(value = "couponId", required = false) Integer couponId,
+            @RequestParam(value = "payerId", required = false) Integer payerId,
+            @RequestParam(value = "tablewareFee", required = false) String tablewareFee,
+            @RequestParam(value = "discountAmount", required = false) String discountAmount,
+            @RequestParam(value = "payAmount", required = false) String payAmount);
+
+    @GetMapping("/payment/searchOrderByOutTradeNoPath")
+    R<Object> searchOrderByOutTradeNoPath(
+            @RequestParam("transactionId") String transactionId,
+            @RequestParam("payType") String payType,
+            @RequestParam("storeId") Integer storeId);
+
     /**
      * 支付成功后重置餐桌与购物车(与 alien-dining {@code StoreOrderService.resetTableAfterPayment} 一致),供微信回调等业务使用。
      */