浏览代码

feat(auth): 更新微信小程序登录和用户管理功能

- 替换了同济电商的RSA密钥对
- 修复了注释中的供应商名称错误
- 添加了RemoteUserWechatVo和AddressAreaDTO导入
- 注入了remoteUserWechatService服务
- 实现了微信小程序注册流程处理
- 统一了registerSource字段命名规范
- 新增了底部导航API接口
- 创建了SysUserWechatService接口定义
- 删除了旧的MiniAuthService服务接口
- 迁移了微信登录逻辑到远程服务调用
- 重构了登录接口返回类型为RemoteUserWechatVo
- 完善了手机号登录和绑定流程
- 添加了租户ID和客户端信息处理
- 实现了用户注册来源参数传递
- 更新了商品分类树查询功能
- 增加了产品自营标识字段
肖路 1 月之前
父节点
当前提交
30c3d3f289
共有 26 个文件被更改,包括 833 次插入502 次删除
  1. 7 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java
  2. 51 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserWechatService.java
  3. 95 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteUserWechatVo.java
  4. 3 3
      ruoyi-auth/src/main/java/org/dromara/auth/controller/Auth2Controller.java
  5. 133 89
      ruoyi-auth/src/main/java/org/dromara/auth/controller/MiniTokenController.java
  6. 6 73
      ruoyi-auth/src/main/java/org/dromara/auth/domain/bo/SysUserWechatBo.java
  7. 0 17
      ruoyi-auth/src/main/java/org/dromara/auth/service/MiniAuthService.java
  8. 0 294
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/MiniAuthServiceImpl.java
  9. 1 1
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerRegisterBo.java
  10. 18 2
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java
  11. 4 4
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/tongji/TongJiPullController.java
  12. 3 4
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/tongji/TongJiPushController.java
  13. 1 1
      ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/zhongche/ZhongChePullController.java
  14. 2 1
      ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/PageCategoryController.java
  15. 4 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/PcProductBo.java
  16. 45 2
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/PcProductVo.java
  17. 4 0
      ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBaseServiceImpl.java
  18. 13 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/pc/IndexSystemController.java
  19. 9 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserWechat.java
  20. 109 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserWechatBo.java
  21. 7 3
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserWechatVo.java
  22. 26 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java
  23. 110 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserWechatServiceImpl.java
  24. 3 4
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserWechatMapper.java
  25. 62 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserWechatService.java
  26. 117 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserWechatServiceImpl.java

+ 7 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java

@@ -44,6 +44,13 @@ public interface RemoteUserService {
      */
     LoginUser getUserInfoByPhonenumber(String phonenumber, String tenantId) throws UserException;
 
+    /**
+     * 通过手机号查询用户信息
+     *
+     * @param phonenumber 手机号
+     * @return 结果
+     */
+    LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException;
     /**
      * 通过邮箱查询用户信息
      *

+ 51 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserWechatService.java

@@ -0,0 +1,51 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.vo.RemoteUserWechatVo;
+
+/**
+ * 小程序用户关联服务
+ *
+ * @author LionLi
+ */
+public interface RemoteUserWechatService {
+
+    /**
+     * 通过openid查询小程序用户信息
+     *
+     * @param openId 微信openid
+     * @return 结果
+     */
+    RemoteUserWechatVo getUserInfoByOpenid(String openId);
+
+    /**
+     * 通过手机号查询小程序用户信息
+     *
+     * @param phoneNumber 手机号
+     * @return 结果
+     */
+    RemoteUserWechatVo getUserInfoByPhoneNumber(String phoneNumber);
+
+    /**
+     * 新增小程序用户关联
+     *
+     * @param userWechat 小程序用户关联信息
+     * @return 结果
+     */
+    Boolean insertUserWechat(RemoteUserWechatVo userWechat);
+
+    /**
+     * 修改小程序用户关联
+     *
+     * @param userWechat 小程序用户关联信息
+     * @return 结果
+     */
+    Boolean updateUserWechat(RemoteUserWechatVo userWechat);
+
+    /**
+     * 通过ID查询小程序用户信息
+     *
+     * @param id 主键
+     * @return 结果
+     */
+    RemoteUserWechatVo getUserInfoById(Long id);
+}

+ 95 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteUserWechatVo.java

@@ -0,0 +1,95 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 小程序用户关联视图对象
+ *
+ * @author LionLi
+ */
+@Data
+@NoArgsConstructor
+public class RemoteUserWechatVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 微信小程序唯一标识
+     */
+    private String openId;
+    /**
+    * 公众号唯一标识
+    * */
+    private String gzhOpenId;
+
+    /**
+     * 微信开放平台唯一标识
+     */
+    private String unionId;
+
+    /**
+     * 微信昵称
+     */
+    private String nickname;
+
+    /**
+     * 头像URL
+     */
+    private String avatarUrl;
+
+    /**
+     * 性别:  0. 男; 1. 女;2. 未知;
+     */
+    private Long gender;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 电话号码
+     */
+    private String phoneNumber;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 微信API临时访问令牌
+     */
+    private String accessToken;
+
+    /**
+     * access_token的有效期(秒)
+     */
+    private Long expireIn;
+
+    /**
+     * AppID
+     */
+    private String appId;
+}

+ 3 - 3
ruoyi-auth/src/main/java/org/dromara/auth/controller/Auth2Controller.java

@@ -49,9 +49,9 @@ public class Auth2Controller {
     private final static String ZHONGCHE_DEVELOPER_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE9ITEKJdH9o1K9AeQYY7zNMo/q5/cdce+9jbawURTPEpBKAx4VkB+lRkb5e5YL+Be4pPM464rPvLyfqGNJvL6uQ==";
 
     //同济电商私钥
-    private final static String TONGJI_DEVELOPER_PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgQu0H97EPqkgz1YS5LkzZNmkG3mS5Er8rJ2LSoJtuOlGgCgYIKoEcz1UBgi2hRANCAARP6NYwTHpW2QTL8A2f2hpgunEpDVkJBhErBQPLqNS/Si5Q+9I9wUpCYdk1EvB5Hw6yzkE4bYk5IZM1j+/SnNFn";
+    private final static String TONGJI_DEVELOPER_PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgkRuHZ2UkqHOv+TpwYFkccmIJpE4Tje63UFV++O1AxTKgCgYIKoEcz1UBgi2hRANCAASL+QeNJvix0bhOunIyO/s1as0aGkQ6am3rf/4eb17UOnJzVo6xvKOFPRA/5rG0rFggU5hYwe70ElN/xWT/Cy2g";
     //同济企采公钥
-    private final static String TONGJI_DEVELOPER_PUBLIC_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgQu0H97EPqkgz1YS5LkzZNmkG3mS5Er8rJ2LSoJtuOlGgCgYIKoEcz1UBgi2hRANCAARP6NYwTHpW2QTL8A2f2hpgunEpDVkJBhErBQPLqNS/Si5Q+9I9wUpCYdk1EvB5Hw6yzkE4bYk5IZM1j+/SnNFn";
+    private final static String TONGJI_DEVELOPER_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAECeORp+9YlmSobsiULe0D1obWOxjTG4aAeufqhtXEwy9BhpbLKQjjifXHKcb92ozx0VBtt5Yt9E7U06+hc0x/tQ==";
     /**
      * 获取 Access Token
      * @param username 用户名
@@ -216,7 +216,7 @@ public class Auth2Controller {
     }
 
     /**
-     * 获取 Access Token  ZHONGChe
+     * 获取 Access Token  TONGJI
      * 注册地址:武汉市硚口区古田二路长丰村长丰乡15栋1号5室
      * 供应商名称:优易达(武汉)有限公司
      * 管理员手机号:18062697722

+ 133 - 89
ruoyi-auth/src/main/java/org/dromara/auth/controller/MiniTokenController.java

@@ -15,20 +15,22 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.seata.spring.annotation.GlobalTransactional;
-import org.dromara.auth.domain.SysUserWechat;
 import org.dromara.auth.domain.bo.SysUserWechatBo;
-import org.dromara.auth.domain.vo.SysUserWechatVo;
-import org.dromara.auth.mapper.SysUserWechatMapper;
-import org.dromara.auth.service.MiniAuthService;
 import org.dromara.auth.util.HttpClientUtil;
 import org.dromara.common.core.domain.R;
+import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.ServletUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.customer.api.RemoteCustomerService;
 import org.dromara.resource.api.RemoteFileService;
 import org.dromara.resource.api.domain.RemoteFile;
+import org.dromara.system.api.RemoteClientService;
 import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.RemoteUserWechatService;
+import org.dromara.system.api.domain.vo.RemoteClientVo;
+import org.dromara.system.api.domain.vo.RemoteUserWechatVo;
 import org.dromara.system.api.model.LoginUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -57,16 +59,21 @@ import java.util.Objects;
 public class MiniTokenController {
     public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
 
-    private final MiniAuthService miniAuthService;
-
-    @Autowired
-    private SysUserWechatMapper wechatMapper;
     @DubboReference
     private RemoteFileService remoteFileService;
 
     @DubboReference
     private RemoteUserService remoteUserService;
 
+    @DubboReference
+    private final RemoteClientService remoteClientService;
+
+    @DubboReference
+    private RemoteUserWechatService remoteUserWechatService;
+
+    @DubboReference
+    private RemoteCustomerService remoteCustomerService;
+
     @Value("${tr.wechat.secret}")
     private String appSecret;
 
@@ -78,19 +85,48 @@ public class MiniTokenController {
      * */
     @SaIgnore
     @PostMapping("/login")
-    public R<SysUserWechatVo> login(@RequestBody SysUserWechatBo wechatBo) {
+    public R<RemoteUserWechatVo> login(@RequestBody SysUserWechatBo wechatBo) {
         log.info("微信用户登录:{}", wechatBo.getCode());
 
         // 从请求头中获取 tenantId
-        String tenantId = ServletUtils.getHeader(Objects.requireNonNull(ServletUtils.getRequest()), "tenantId");
-        log.info("从请求头获取到 tenantId: {}", tenantId);
+//        String tenantId = ServletUtils.getHeader(Objects.requireNonNull(ServletUtils.getRequest()), "tenantId");
+//        log.info("从请求头获取到 tenantId: {}", tenantId);
+
+        String tenantId = "000000";
 
         // 在动态租户上下文中执行登录逻辑
         return TenantHelper.dynamic(tenantId, () -> {
             try {
-                // 1. 调用 Service 层逻辑
-                // 注意:这里需要修改 Service,如果用户不存在,让它抛出特定异常
-                SysUserWechat wechat = miniAuthService.wxLogin(wechatBo);
+                RemoteClientVo client = remoteClientService.queryByClientId(wechatBo.getClientId());
+                if (client == null) {
+                    return R.fail("客户端不存在");
+                }
+                // 1. 调用 Service 层逻辑获取 openid
+                String openid = getOpenid(wechatBo.getCode());
+                if (openid == null) {
+                    return R.fail("登录code无效,登录失败");
+                }
+
+                // 2. 通过 openid 查询用户
+                RemoteUserWechatVo wechat = remoteUserWechatService.getUserInfoByOpenid(openid);
+
+                // 3. 如果用户不存在,返回引导注册信息
+                if (wechat == null) {
+                    log.info("检测到新用户 (OpenID: {}), 引导注册", openid);
+                    RemoteUserWechatVo wechatVo = new RemoteUserWechatVo();
+                    wechatVo.setOpenId(openid);
+                    wechatVo.setAppId(appId);
+                    return R.warn("用户未注册,请前往注册", wechatVo);
+                }
+
+                Long customerId = null;
+                if(ObjectUtil.isNotEmpty(wechat.getUserId())){
+                    customerId = remoteCustomerService.selectCustomerIdByUserId(wechat.getUserId());
+                    if (Objects.isNull(customerId)) {
+                        return R.warn("该手机号未注册客户,请先注册");
+                    }
+                }
+
 
                 // --- 登录成功逻辑 (用户已存在) ---
                 LoginUser loginUser = new LoginUser();
@@ -99,37 +135,25 @@ public class MiniTokenController {
                 loginUser.setUserType("app_user");
                 loginUser.setOpenid(wechat.getOpenId());
                 loginUser.setTenantId(tenantId);
+                loginUser.setClientKey(client.getClientKey());
+                loginUser.setDeviceType(client.getDeviceType());
+                loginUser.setCustomerId(customerId);
 
                 SaLoginParameter model = new SaLoginParameter();
-                model.setDeviceType("xcx");
-                model.setTimeout(30 * 24 * 60 * 60);
-                model.setActiveTimeout(7 * 24 * 60 * 60);
-                model.setExtra(LoginHelper.CLIENT_KEY, "miniprogram_client_id_2025");
+                model.setTimeout(client.getTimeout());
+                model.setActiveTimeout(client.getActiveTimeout());
+                model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
 
                 LoginHelper.login(loginUser, model);
                 String accessToken = StpUtil.getTokenValue();
                 Long expireIn = StpUtil.getTokenTimeout();
 
-                SysUserWechatVo wechatVo = BeanUtil.copyProperties(wechat, SysUserWechatVo.class);
-                wechatVo.setOpenId(wechat.getOpenId());
-                wechatVo.setAccessToken(accessToken);
-                wechatVo.setExpireIn(expireIn);
+                wechat.setAccessToken(accessToken);
+                wechat.setExpireIn(expireIn);
 
-                return R.ok(wechatVo);
+                return R.ok(wechat);
 
             } catch (Exception e) {
-                // --- 异常处理逻辑 ---
-                // 判断是否是“用户未注册”的特定异常
-                // 这里假设你在 Service 层抛出的异常信息包含 "USER_NOT_REGISTERED"
-                if (e.getMessage() != null && e.getMessage().contains("USER_NOT_REGISTERED")) {
-                    log.info("检测到新用户,返回 AppID 引导注册");
-                    SysUserWechatVo wechatVo = new SysUserWechatVo();
-                    wechatVo.setOpenId(wechatBo.getCode());
-                    wechatVo.setAppId(appId);
-                    // 返回特定状态码 (例如 201) 和 AppID
-                    return R.warn("用户未注册,请前往注册", wechatVo);
-                }
-
                 // 其他异常正常抛出
                 log.error("登录异常", e);
                 return R.fail("登录失败: " + e.getMessage());
@@ -142,15 +166,21 @@ public class MiniTokenController {
      * */
     @SaIgnore
     @PostMapping("/loginByPhone")
-    public R<SysUserWechatVo> loginByPhone(@RequestBody Map<String, String> data) {
+    public R<RemoteUserWechatVo> loginByPhone(@RequestBody Map<String, String> data) {
         try {
             String code = data.get("code");
             String openId = data.get("openId");
+            String clientId = data.get("clientId");
             log.info("手机号快捷登录,获取临时code: {}", code);
 
             // 获取 tenantId
-            String tenantId = ServletUtils.getHeader(Objects.requireNonNull(ServletUtils.getRequest()), "tenantId");
+//            String tenantId = ServletUtils.getHeader(Objects.requireNonNull(ServletUtils.getRequest()), "tenantId");
 
+            String tenantId = "000000";
+            RemoteClientVo client = remoteClientService.queryByClientId(clientId);
+            if (client == null) {
+                return R.fail("客户端不存在");
+            }
             return TenantHelper.dynamic(tenantId, () -> {
                 // 1. 通过 code 换取手机号
                 String phoneNumber = getPhoneNumberByCode(code);
@@ -158,34 +188,37 @@ public class MiniTokenController {
                     return R.fail("获取手机号失败");
                 }
                 log.info("获取到手机号: {}", phoneNumber);
-                SysUserWechat user = null;
-                LoginUser userInfoByPhonenumber = remoteUserService.getUserInfoByPhonenumber(phoneNumber, "000000");
+                RemoteUserWechatVo userVo = null;
+                LoginUser userInfoByPhonenumber = remoteUserService.getUserInfoByPhonenumber(phoneNumber);
+                if (ObjectUtil.isEmpty(userInfoByPhonenumber)) {
+                    return R.warn("该手机号未注册,请先注册");
+                }
                 if (ObjectUtil.isNotEmpty(userInfoByPhonenumber)) {
-                    SysUserWechat sysUserWechat = miniAuthService.queryByPhoneNumber(phoneNumber);
+                    RemoteUserWechatVo sysUserWechat = remoteUserWechatService.getUserInfoByPhoneNumber(phoneNumber);
                     if (ObjectUtil.isEmpty(sysUserWechat)) {
-                        SysUserWechat userWechat = new SysUserWechat();
-                        userWechat.setOpenId(openId);
-                        userWechat.setStatus("0");
-                        // 设置租户ID
-                        userWechat.setTenantId(tenantId);
+                        // 创建新的微信关联记录
+                        RemoteUserWechatVo newUserWechat = new RemoteUserWechatVo();
+                        newUserWechat.setOpenId(openId);
+                        newUserWechat.setStatus("0");
 
                         // --- 核心修复:处理昵称 ---
                         // 静默登录没传昵称,生成默认昵称
                         String nickName = "用户_" + openId.substring(openId.length() - 8);
-                        userWechat.setUserId(userInfoByPhonenumber.getUserId());
-                        userWechat.setPhoneNumber(phoneNumber);
+                        newUserWechat.setUserId(userInfoByPhonenumber.getUserId());
+                        newUserWechat.setPhoneNumber(phoneNumber);
                         // 保存微信关联表
-                        userWechat.setNickname(nickName); // 保存默认昵称
+                        newUserWechat.setNickname(nickName); // 保存默认昵称
                         // --- 处理头像 ---
-                        userWechat.setAvatarUrl("/default/avatar.png"); // 默认头像
-                        int insert = wechatMapper.insert(userWechat);
-                        if (insert > 0) {
-                            user = userWechat;
+                        newUserWechat.setAvatarUrl("/default/avatar.png"); // 默认头像
+
+                        Boolean insert = remoteUserWechatService.insertUserWechat(newUserWechat);
+                        if (insert) {
+                            userVo = newUserWechat;
                         }else{
                             return R.fail("保存微信关联失败");
                         }
                     } else {
-                        user = sysUserWechat;
+                        userVo = sysUserWechat;
                     }
                 }
 
@@ -196,30 +229,35 @@ public class MiniTokenController {
                 // if (user == null) {
                 //     user = miniAuthService.registerByPhoneNumber(phoneNumber);
                 // }
-
-                if (user == null) {
-                    return R.fail("该手机号未注册,请先注册");
+                Long customerId = null;
+                if (ObjectUtil.isNotEmpty(userVo.getUserId())) {
+                    customerId = remoteCustomerService.selectCustomerIdByUserId(userVo.getUserId());
+                    if (Objects.isNull(customerId)) {
+                        return R.warn("该手机号未注册客户,请先注册");
+                    }
                 }
 
                 // 3. 登录逻辑(与 /login 接口保持一致)
                 LoginUser loginUser = new LoginUser();
-                loginUser.setUserId(user.getUserId());
-                loginUser.setNickname(user.getNickname());
+                loginUser.setUserId(userVo.getUserId());
+                loginUser.setNickname(userVo.getNickname());
                 loginUser.setUserType("app_user");
-                loginUser.setOpenid(user.getOpenId());
+                loginUser.setOpenid(userVo.getOpenId());
                 loginUser.setTenantId(tenantId);
+                loginUser.setClientKey(client.getClientKey());
+                loginUser.setDeviceType(client.getDeviceType());
+                loginUser.setCustomerId(customerId);
 
-                SaLoginParameter model = new SaLoginParameter();
-                model.setDeviceType("xcx");
-                model.setTimeout(30 * 24 * 60 * 60);
-                model.setActiveTimeout(7 * 24 * 60 * 60);
-                model.setExtra(LoginHelper.CLIENT_KEY, "miniprogram_client_id_2025");
 
+                SaLoginParameter model = new SaLoginParameter();
+                model.setDeviceType(client.getDeviceType());
+                model.setTimeout(client.getTimeout());
+                model.setActiveTimeout(client.getActiveTimeout());
+                model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
                 LoginHelper.login(loginUser, model);
                 String accessToken = StpUtil.getTokenValue();
                 Long expireIn = StpUtil.getTokenTimeout();
 
-                SysUserWechatVo userVo = BeanUtil.copyProperties(user, SysUserWechatVo.class);
                 userVo.setAccessToken(accessToken);
                 userVo.setExpireIn(expireIn);
 
@@ -235,17 +273,22 @@ public class MiniTokenController {
     @SaIgnore
     @PostMapping("/bindPhone")
     @GlobalTransactional
-    public R<SysUserWechatVo> getUserPhone(@RequestBody Map<String, Object> phoneData) {
+    public R<RemoteUserWechatVo> getUserPhone(@RequestBody Map<String, Object> phoneData) {
         try {
             String code = (String) phoneData.get("code");
             Long userId = Long.valueOf(phoneData.get("userId").toString());
+            String clientId = (String) phoneData.get("clientId");
 
             log.info("获取手机号请求 - userId: {}, code: {}", userId, code);
 
             // 从请求头中获取 tenantId
-            String tenantId = ServletUtils.getHeader(Objects.requireNonNull(ServletUtils.getRequest()), "tenantId");
-            log.info("从请求头获取到 tenantId: {}", tenantId);
-
+//            String tenantId = ServletUtils.getHeader(Objects.requireNonNull(ServletUtils.getRequest()), "tenantId");
+//            log.info("从请求头获取到 tenantId: {}", tenantId);
+            String tenantId = "000000";
+            RemoteClientVo client = remoteClientService.queryByClientId(clientId);
+            if (client == null) {
+                return R.fail("客户端不存在");
+            }
             // 在动态租户上下文中执行绑定手机号逻辑
             return TenantHelper.dynamic(tenantId, () -> {
                 // 1. 通过手机号专用code直接获取手机号(新方法)
@@ -262,17 +305,21 @@ public class MiniTokenController {
                 log.info("解密得到手机号: {}", phoneNumber.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
 
                 // 3. 更新用户手机号
-                SysUserWechatVo user = miniAuthService.queryById(userId);
+                RemoteUserWechatVo user = remoteUserWechatService.getUserInfoById(userId);
                 if (user == null) {
                     return R.fail("用户不存在");
                 }
+                Long customerId = null;
+                if (ObjectUtil.isNotEmpty(user.getUserId())) {
+                    customerId = remoteCustomerService.selectCustomerIdByUserId(user.getUserId());
+                    if (Objects.isNull(customerId)) {
+                        return R.warn("该手机号未注册客户,请先注册");
+                    }
+                }
 
                 user.setPhoneNumber(phoneNumber);
-                // 创建BO对象进行更新
-                SysUserWechatBo updateBo = new SysUserWechatBo();
-                updateBo.setUserId(userId);
-                updateBo.setPhoneNumber(phoneNumber);
-                miniAuthService.updateByBo(updateBo);
+                // 通过远程调用更新
+                remoteUserWechatService.updateUserWechat(user);
 
                 // 4. 重新生成token并返回用户信息
                 LoginUser loginUser = new LoginUser();
@@ -281,13 +328,15 @@ public class MiniTokenController {
                 loginUser.setUserType("app_user");
                 loginUser.setOpenid(user.getOpenId());
                 loginUser.setTenantId(tenantId); // 设置租户ID到登录用户
-
+                loginUser.setClientKey(client.getClientKey());
+                loginUser.setDeviceType(client.getDeviceType());
+                loginUser.setCustomerId(customerId);
                 // 配置登录参数
                 SaLoginParameter model = new SaLoginParameter();
-                model.setDeviceType("xcx");
-                model.setTimeout(30 * 24 * 60 * 60);
-                model.setActiveTimeout(7 * 24 * 60 * 60);
-                model.setExtra(LoginHelper.CLIENT_KEY, "miniprogram_client_id_2025");
+                model.setDeviceType(client.getDeviceType());
+                model.setTimeout(client.getTimeout());
+                model.setActiveTimeout(client.getActiveTimeout());
+                model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
 
                 // 生成token
                 LoginHelper.login(loginUser, model);
@@ -295,7 +344,7 @@ public class MiniTokenController {
                 // 获取token信息
                 String accessToken = StpUtil.getTokenValue();
                 Long expireIn = StpUtil.getTokenTimeout();
-                SysUserWechatVo userVo = new SysUserWechatVo();
+                RemoteUserWechatVo userVo = new RemoteUserWechatVo();
                 userVo.setOpenId(user.getOpenId());
                 userVo.setUserId(user.getUserId());
                 userVo.setStatus(user.getStatus());
@@ -460,14 +509,9 @@ public class MiniTokenController {
      */
     private boolean isPhoneNumberExists(String phoneNumber, Long excludeUserId) {
         try {
-            // 这里需要调用service层方法查询数据库
-            // 由于没有现成的方法,暂时返回false,建议后续完善
-            // WxVsUser existingUser = vsUserService.selectByPhoneNumber(phoneNumber);
-            // return existingUser != null && !existingUser.getUserId().equals(excludeUserId);
-
             log.info("检查手机号是否存在: {} (排除用户: {})",
                 phoneNumber.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"), excludeUserId);
-            SysUserWechat existingUser = miniAuthService.queryByPhoneNumber(phoneNumber);
+            RemoteUserWechatVo existingUser = remoteUserWechatService.getUserInfoByPhoneNumber(phoneNumber);
             return existingUser != null && !existingUser.getUserId().equals(excludeUserId);
         } catch (Exception e) {
             log.error("检查手机号重复时发生异常", e);
@@ -507,8 +551,8 @@ public class MiniTokenController {
     }
 
     @GetMapping("/userInfo/{userId}")
-    public R<SysUserWechatVo> getInfo(@NotNull(message = "主键不能为空")
+    public R<RemoteUserWechatVo> getInfo(@NotNull(message = "主键不能为空")
                                       @PathVariable("userId") Long id) {
-        return R.ok(miniAuthService.queryById(id));
+        return R.ok(remoteUserWechatService.getUserInfoById(id));
     }
 }

+ 6 - 73
ruoyi-auth/src/main/java/org/dromara/auth/domain/bo/SysUserWechatBo.java

@@ -1,99 +1,32 @@
 package org.dromara.auth.domain.bo;
 
-import io.github.linpeilie.annotations.AutoMapper;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
 import lombok.Data;
-import lombok.EqualsAndHashCode;
-import org.dromara.auth.domain.SysUserWechat;
-import org.dromara.common.core.validate.AddGroup;
-import org.dromara.common.core.validate.EditGroup;
-import org.dromara.common.mybatis.core.domain.BaseEntity;
 
 /**
- * 小程序用户关联业务对象 sys_user_wechat
+ * 小程序用户登录业务对象
  *
  * @author LionLi
  */
 @Data
-@EqualsAndHashCode(callSuper = true)
-@AutoMapper(target = SysUserWechat.class, reverseConvertGenerate = false)
-public class SysUserWechatBo extends BaseEntity {
+public class SysUserWechatBo {
 
     /**
-     * 主键ID
+     * 微信登录code
      */
-    private Long id;
-
-    private Long userId;
-
-    /**
-     * 微信小程序唯一标识
-     */
-    private String openId;
+    private String code;
 
     /**
-     * 微信开放平台唯一标识
+     * 客户端id
      */
-    @NotBlank(message = "微信开放平台唯一标识不能为空", groups = {AddGroup.class, EditGroup.class})
-    private String unionId;
+    private String clientId;
 
     /**
      * 微信昵称
      */
-    @NotBlank(message = "微信昵称不能为空", groups = {AddGroup.class, EditGroup.class})
     private String nickname;
 
     /**
      * 头像URL
      */
-    @NotBlank(message = "头像URL不能为空", groups = {AddGroup.class, EditGroup.class})
     private String avatarUrl;
-
-    /**
-     * 性别:  0. 男; 1. 女;2. 未知;
-     */
-    @NotNull(message = "性别(0=未知,1=男,2=女)不能为空", groups = {AddGroup.class, EditGroup.class})
-    private Long gender;
-
-    /**
-     * 微信会话密钥(仅后端临时存储,需加密!)
-     */
-    @NotBlank(message = "微信会话密钥(仅后端临时存储,需加密!)不能为空", groups = {AddGroup.class, EditGroup.class})
-    private String sessionKey;
-
-    /**
-     * 微信API临时访问令牌
-     */
-    @NotBlank(message = "微信API临时访问令牌不能为空", groups = {AddGroup.class, EditGroup.class})
-    private String accessToken;
-
-    /**
-     * access_token的有效期(秒)
-     */
-    @NotNull(message = "access_token的有效期(秒)不能为空", groups = {AddGroup.class, EditGroup.class})
-    private Long expireIn;
-
-    /**
-     * 用于刷新access_token的令牌
-     */
-    @NotBlank(message = "用于刷新access_token的令牌不能为空", groups = {AddGroup.class, EditGroup.class})
-    private String refreshToken;
-
-    /**
-     * 状态(0正常 1停用)
-     */
-    @NotBlank(message = "状态(0正常 1停用)不能为空", groups = {AddGroup.class, EditGroup.class})
-    private String status;
-
-    /**
-     * 备注
-     */
-    @NotBlank(message = "备注不能为空", groups = {AddGroup.class, EditGroup.class})
-    private String remark;
-
-    private String code;
-
-    private String phoneNumber;
-
 }

+ 0 - 17
ruoyi-auth/src/main/java/org/dromara/auth/service/MiniAuthService.java

@@ -1,17 +0,0 @@
-package org.dromara.auth.service;
-
-
-import org.dromara.auth.domain.SysUserWechat;
-import org.dromara.auth.domain.bo.SysUserWechatBo;
-import org.dromara.auth.domain.vo.SysUserWechatVo;
-
-public interface MiniAuthService {
-
-    SysUserWechat wxLogin(SysUserWechatBo wechatBo);
-
-    SysUserWechatVo queryById(Long id);
-
-    Boolean updateByBo(SysUserWechatBo bo);
-
-    SysUserWechat queryByPhoneNumber(String phoneNumber);
-}

+ 0 - 294
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/MiniAuthServiceImpl.java

@@ -1,294 +0,0 @@
-package org.dromara.auth.service.impl;
-
-import cn.hutool.crypto.digest.BCrypt;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.dubbo.config.annotation.DubboReference;
-import org.apache.seata.spring.annotation.GlobalTransactional;
-import org.dromara.auth.domain.SysUserWechat;
-import org.dromara.auth.domain.bo.SysUserWechatBo;
-import org.dromara.auth.domain.vo.SysUserWechatVo;
-import org.dromara.auth.mapper.SysUserWechatMapper;
-import org.dromara.auth.properties.WeChatProperties;
-import org.dromara.auth.service.MiniAuthService;
-import org.dromara.auth.util.HttpClientUtil;
-import org.dromara.common.core.utils.MapstructUtils;
-import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.oss.core.OssClient;
-import org.dromara.common.oss.entity.UploadResult;
-import org.dromara.common.oss.factory.OssFactory;
-import org.dromara.common.satoken.utils.LoginHelper;
-import org.dromara.common.tenant.helper.TenantHelper;
-import org.dromara.system.api.RemoteUserService;
-import org.dromara.system.api.domain.bo.RemoteUserBo;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.Base64;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @program: photo-studio
- * @description:
- * @author: king
- * @create: 2025-12-05 10:43
- * @version: 1.0
- **/
-@Slf4j
-@Service
-public class MiniAuthServiceImpl implements MiniAuthService {
-    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
-
-    @DubboReference
-    private RemoteUserService remoteUserService;
-
-    @Autowired
-    private WeChatProperties weChatProperties;
-    @Autowired
-    private SysUserWechatMapper wechatMapper;
-
-    @Override
-    @GlobalTransactional
-    @Transactional(rollbackFor = Exception.class)
-    public SysUserWechat wxLogin(SysUserWechatBo wechatBo) {
-
-        String openid = getOpenid(wechatBo.getCode()); //
-
-        if (openid == null) {
-            throw new RuntimeException("登录code无效,登录失败");
-        }
-
-        LambdaQueryWrapper<SysUserWechat> lqw =
-            new LambdaQueryWrapper<SysUserWechat>().eq(SysUserWechat::getOpenId, openid);
-
-        // 判断当前用户是否为新用户
-        SysUserWechat user = wechatMapper.selectOne(lqw);
-
-        // 如果是新用户,抛出特定异常,不再自动注册
-        if (user == null) {
-            log.info("检测到新用户 (OpenID: {}), 引导注册", openid);
-            // 抛出一个包含特定标记的异常,供 Controller 捕获
-            throw new RuntimeException("USER_NOT_REGISTERED: 用户未注册");
-        }
-
-        // --- 老用户逻辑保持不变 ---
-        boolean needUpdate = false;
-        if (StringUtils.isNotBlank(wechatBo.getNickname()) && !wechatBo.getNickname().equals(user.getNickname())) {
-            user.setNickname(wechatBo.getNickname());
-            needUpdate = true;
-        }
-        if (StringUtils.isNotBlank(wechatBo.getAvatarUrl()) && !wechatBo.getAvatarUrl().equals(user.getAvatarUrl())) {
-            String ossUrl = transferImageToOss(wechatBo.getAvatarUrl());
-            if (StringUtils.isNotBlank(ossUrl)) {
-                user.setAvatarUrl(ossUrl.length() > 200 ? ossUrl.substring(0, 200) : ossUrl);
-                needUpdate = true;
-            }
-        }
-        if (needUpdate) {
-            wechatMapper.updateById(user);
-            log.info("用户信息更新成功: {}", user.getNickname());
-        }
-
-        return user;
-    }
-
-
-    //    private String getOpenid(String code) {
-//        log.info("微信接口服务,获得当前微信用户的openid{}", code);
-//        // 调用微信接口服务,获得当前微信用户的openid
-//        log.info("微信接口服务,获得当前微信用户的Appid{}", weChatProperties.getAppid());
-//        log.info("微信接口服务,获得当前微信用户的Secret{}", weChatProperties.getSecret());
-//
-//        Map<String, String> map = new HashMap<>();
-//        map.put("appid", weChatProperties.getAppid());
-//        map.put("secret", weChatProperties.getSecret());
-//        map.put("js_code", code);
-//        map.put("grant_type", "authorization_code");
-//        String json = HttpClientUtil.doGet(WX_LOGIN, map);
-//        log.info("微信接口服务,获得当前微信用户的json{}", json);
-//        JSONObject jsonObject = JSON.parseObject(json);
-//        String openid = jsonObject.getString("openid");
-//        log.info("微信接口服务,获得当前微信用户的openid{}", openid);
-//        return openid;
-//    }
-    private String getOpenid(String code) {
-        // 1. 获取配置(注意:这里获取的是后端配置的 AppID,必须与前端小程序的 AppID 一致)
-        String appId = weChatProperties.getAppid();
-        String secret = weChatProperties.getSecret();
-
-        // 2. 构建参数
-        Map<String, String> map = new HashMap<>();
-        map.put("appid", appId);
-        map.put("secret", secret);
-        map.put("js_code", code);
-        map.put("grant_type", "authorization_code");
-
-        try {
-            // 3. 调用微信接口
-            String json = HttpClientUtil.doGet(WX_LOGIN, map);
-            log.info("微信接口返回: {}", json);
-
-            // 4. 解析结果
-            JSONObject jsonObject = JSON.parseObject(json);
-
-            // 5. 【关键】先检查有没有错误码
-            if (jsonObject.containsKey("errcode")) {
-                int errCode = jsonObject.getIntValue("errcode");
-                String errMsg = jsonObject.getString("errmsg");
-                log.error("微信登录失败: code={}, error={}", errCode, errMsg);
-                // 抛出异常或返回 null,让上层知道登录失败
-                throw new RuntimeException("微信登录失败: " + errMsg);
-            }
-
-            // 6. 获取 OpenID
-            String openid = jsonObject.getString("openid");
-            if (StringUtils.isEmpty(openid)) {
-                throw new RuntimeException("微信返回openid为空");
-            }
-
-            // 7. (可选) 获取 UnionID - 如果你需要打通公众号/App 账号体系
-            String unionId = jsonObject.getString("unionid");
-
-            return openid;
-
-        } catch (Exception e) {
-            log.error("调用微信接口异常", e);
-            throw new RuntimeException("网络连接微信服务失败", e);
-        }
-    }
-
-    /**
-     * 将传入的头像地址转存到OSS并返回最终URL
-     * 支持 http(s) 链接 与 data:image/...;base64 格式
-     */
-    private String transferImageToOss(String source) {
-        if (StringUtils.isBlank(source)) {
-            return null;
-        }
-
-        // 如果已经是OSS地址,直接返回
-        if (source.startsWith("https://oss.") || source.contains(".aliyuncs.com") ||
-            source.contains(".qiniucdn.com") || source.contains("hxsjkj.com")) {
-            log.info("头像已是OSS地址,直接使用: {}", source.substring(0, Math.min(50, source.length())));
-            return source;
-        }
-
-        try {
-            byte[] imageBytes;
-            String suffix = ".jpg";
-            String contentType = "image/jpeg";
-
-            // 1) 处理 Base64 格式
-            if (source.startsWith("data:image/")) {
-                log.info("检测到Base64图片,开始解码并上传");
-                int comma = source.indexOf(',');
-                if (comma > 0) {
-                    String meta = source.substring(5, comma); // image/png;base64
-                    if (meta.contains("/")) {
-                        String ext = meta.substring(meta.indexOf('/') + 1);
-                        if (ext.contains(";")) {
-                            ext = ext.substring(0, ext.indexOf(';'));
-                        }
-                        suffix = "." + ext;
-                        contentType = "image/" + ext;
-                    }
-                    String base64 = source.substring(comma + 1);
-                    imageBytes = Base64.getDecoder().decode(base64);
-                } else {
-                    log.warn("Base64格式不正确");
-                    return null;
-                }
-            }
-            // 2) 处理 HTTP(S) 外部链接(微信头像等)
-            else if (source.startsWith("http://") || source.startsWith("https://")) {
-                log.info("检测到外部图片URL,开始下载: {}", source.substring(0, Math.min(50, source.length())));
-                imageBytes = downloadImageBytes(source);
-                if (imageBytes == null || imageBytes.length == 0) {
-                    log.warn("下载图片失败");
-                    return null;
-                }
-                // 根据URL推断扩展名
-                if (source.contains(".png")) {
-                    suffix = ".png";
-                    contentType = "image/png";
-                } else if (source.contains(".webp")) {
-                    suffix = ".webp";
-                    contentType = "image/webp";
-                } else if (source.contains(".gif")) {
-                    suffix = ".gif";
-                    contentType = "image/gif";
-                }
-            } else {
-                log.warn("不支持的图片格式: {}", source.substring(0, Math.min(50, source.length())));
-                return null;
-            }
-
-            // 3) 上传到 OSS
-            if (imageBytes == null || imageBytes.length == 0) {
-                log.warn("图片字节为空,无法上传");
-                return null;
-            }
-
-            log.info("开始上传图片到OSS,大小: {} bytes", imageBytes.length);
-            OssClient storage = OssFactory.instance();
-            UploadResult uploadResult = storage.uploadSuffix(imageBytes, suffix, contentType);
-            String ossUrl = uploadResult.getUrl();
-            log.info("图片上传成功,OSS URL: {}", ossUrl);
-            return ossUrl;
-
-        } catch (Exception e) {
-            log.error("头像转存OSS失败", e);
-            return null;
-        }
-    }
-
-    /**
-     * 下载网络图片为字节数组
-     */
-    private byte[] downloadImageBytes(String url) {
-        try {
-            RestTemplate restTemplate = new RestTemplate();
-            ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
-            if (response.getStatusCode().is2xxSuccessful()) {
-                return response.getBody();
-            }
-            log.warn("下载图片失败,状态码: {}", response.getStatusCode());
-            return null;
-        } catch (Exception e) {
-            log.error("下载图片异常: {}", url, e);
-            return null;
-        }
-    }
-
-    private void validEntityBeforeSave(SysUserWechat entity) {
-        //TODO 做一些数据校验,如唯一约束
-    }
-
-    @Override
-    public SysUserWechatVo queryById(Long id) {
-        return wechatMapper.selectVoById(id);
-    }
-
-
-    @Override
-    public Boolean updateByBo(SysUserWechatBo bo) {
-        SysUserWechat update = MapstructUtils.convert(bo, SysUserWechat.class);
-        validEntityBeforeSave(update);
-        return wechatMapper.updateById(update) > 0;
-    }
-
-    @Override
-    public SysUserWechat queryByPhoneNumber(String phoneNumber) {
-        LambdaQueryWrapper<SysUserWechat> lqw =
-            new LambdaQueryWrapper<SysUserWechat>().eq(SysUserWechat::getPhoneNumber, phoneNumber);
-        return wechatMapper.selectOne(lqw);
-    }
-
-
-}

+ 1 - 1
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerRegisterBo.java

@@ -36,7 +36,7 @@ public class CustomerRegisterBo {
     /**
     * 注册来源 1 平台pc  2小程序
     * */
-    private String RegisterSource;
+    private String registerSource;
 
     private String openId;
 

+ 18 - 2
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java

@@ -35,7 +35,10 @@ import org.dromara.system.api.*;
 import org.dromara.system.api.domain.bo.RemoteUserBo;
 import org.dromara.system.api.domain.dto.AddressAreaDTO;
 import org.dromara.system.api.domain.vo.RemoteDeptVo;
+import org.dromara.system.api.domain.vo.RemoteComStaffVo;
 import org.dromara.system.api.domain.vo.RemoteDictDataVo;
+import org.dromara.system.api.domain.dto.AddressAreaDTO;
+import org.dromara.system.api.domain.vo.RemoteUserWechatVo;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -65,6 +68,8 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
     @DubboReference
     private RemoteUserService remoteUserService;
     @DubboReference
+    private RemoteUserWechatService remoteUserWechatService;
+    @DubboReference
     private RemoteComPostService remoteComPostService;
     @DubboReference
     private RemoteComStaffService remoteComStaffService;
@@ -1380,8 +1385,19 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
         }
         Long userId = remoteUserService.addUser(remoteUserBo);
         if ("2".equals(bo.getRegisterSource())) {//微信小程序注册
-
-
+            RemoteUserWechatVo remoteUserWechatVo = new RemoteUserWechatVo();
+            remoteUserWechatVo.setOpenId(bo.getOpenId());
+            remoteUserWechatVo.setUserId(userId);
+            remoteUserWechatVo.setPhoneNumber(bo.getPurchasePhone());
+            remoteUserWechatVo.setNickname(bo.getPurchaseName());
+            remoteUserWechatService.insertUserWechat(remoteUserWechatVo);
+        }else if ("3".equals(bo.getRegisterSource())){
+            RemoteUserWechatVo remoteUserWechatVo = new RemoteUserWechatVo();
+            remoteUserWechatVo.setGzhOpenId(bo.getOpenId());
+            remoteUserWechatVo.setUserId(userId);
+            remoteUserWechatVo.setPhoneNumber(bo.getPurchasePhone());
+            remoteUserWechatVo.setNickname(bo.getPurchaseName());
+            remoteUserWechatService.insertUserWechat(remoteUserWechatVo);
         }
 
         remoteUserBo.setUserId(userId);

+ 4 - 4
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/tongji/TongJiPullController.java

@@ -49,11 +49,11 @@ public class TongJiPullController {
 
     //正式环境
     // 同济地区查询接口地址(替换为真实域名)
-    private static final String AREA_QUERY_URL = "https://supply.crrcgo.cc/mallapi";
+    private static final String AREA_QUERY_URL = "https://prvlxcg.tjhapp.com.cn/supplier-mall-gateway/";
     // 同济提供的配置(替换为真实值)
-    private static final String CLIENT_ID = "KFZnKGiDsJ7";
-    private static final String PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgQu0H97EPqkgz1YS5LkzZNmkG3mS5Er8rJ2LSoJtuOlGgCgYIKoEcz1UBgi2hRANCAARP6NYwTHpW2QTL8A2f2hpgunEpDVkJBhErBQPLqNS/Si5Q+9I9wUpCYdk1EvB5Hw6yzkE4bYk5IZM1j+/SnNFn"; // 电商平台私钥
-    private static final String TJ_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE9ITEKJdH9o1K9AeQYY7zNMo/q5/cdce+9jbawURTPEpBKAx4VkB+lRkb5e5YL+Be4pPM464rPvLyfqGNJvL6uQ=="; // 同济公钥
+    private static final String CLIENT_ID = "KFZOW9K7ix6";
+    private static final String PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgkRuHZ2UkqHOv+TpwYFkccmIJpE4Tje63UFV++O1AxTKgCgYIKoEcz1UBgi2hRANCAASL+QeNJvix0bhOunIyO/s1as0aGkQ6am3rf/4eb17UOnJzVo6xvKOFPRA/5rG0rFggU5hYwe70ElN/xWT/Cy2g"; // 电商平台私钥
+    private static final String TJ_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAECeORp+9YlmSobsiULe0D1obWOxjTG4aAeufqhtXEwy9BhpbLKQjjifXHKcb92ozx0VBtt5Yt9E7U06+hc0x/tQ=="; // 同济公钥
     //测试环境
     // 同济地区查询接口地址(替换为真实域名)
 //    private static final String AREA_QUERY_URL = "https://supply-test.crrcgo.cc/mallapi/";

+ 3 - 4
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/tongji/TongJiPushController.java

@@ -58,8 +58,7 @@ import static org.dromara.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
 @RestController
 @RequestMapping("/tongji/api/mall")
 public class TongJiPushController {
-    private final String key = GLOBAL_REDIS_KEY+"external:zhongche:token:";
-    private final String CLIENT_ID = "KFZnKGiDsJ7";
+    private final String CLIENT_ID = "KFZOW9K7ix6";
     private final String VERSION = "1.0.0";
 
     @DubboReference
@@ -77,9 +76,9 @@ public class TongJiPushController {
     private final IExternalItemService externalItemService;
 
     //电商私钥
-    private final static String DEVELOPER_PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgQu0H97EPqkgz1YS5LkzZNmkG3mS5Er8rJ2LSoJtuOlGgCgYIKoEcz1UBgi2hRANCAARP6NYwTHpW2QTL8A2f2hpgunEpDVkJBhErBQPLqNS/Si5Q+9I9wUpCYdk1EvB5Hw6yzkE4bYk5IZM1j+/SnNFn";
+    private final static String DEVELOPER_PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgkRuHZ2UkqHOv+TpwYFkccmIJpE4Tje63UFV++O1AxTKgCgYIKoEcz1UBgi2hRANCAASL+QeNJvix0bhOunIyO/s1as0aGkQ6am3rf/4eb17UOnJzVo6xvKOFPRA/5rG0rFggU5hYwe70ElN/xWT/Cy2g";
     //企采公钥
-    private final static String DEVELOPER_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE9ITEKJdH9o1K9AeQYY7zNMo/q5/cdce+9jbawURTPEpBKAx4VkB+lRkb5e5YL+Be4pPM464rPvLyfqGNJvL6uQ==";
+    private final static String DEVELOPER_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAECeORp+9YlmSobsiULe0D1obWOxjTG4aAeufqhtXEwy9BhpbLKQjjifXHKcb92ozx0VBtt5Yt9E7U06+hc0x/tQ==";
 
 
     //测试环境

+ 1 - 1
ruoyi-modules/ruoyi-external/src/main/java/org/dromara/external/controller/zhongche/ZhongChePullController.java

@@ -606,7 +606,7 @@ public class ZhongChePullController {
         return zcr;
     }
 
-    //5.4.5	换货新品发货
+        //5.4.5	换货新品发货
     @PostMapping("/mall/aftersale/deliver/goods")
     public AfterSaleDeliverGoodsVo mallAftersaleDeliverGoods(@RequestBody AfterSaleDeliverGoodsBo bo) {
         ZCR responseDto = doZcPost("/api/mall/aftersale/deliver/goods", bo);

+ 2 - 1
ruoyi-modules/ruoyi-mall/src/main/java/org/dromara/mall/controller/PageCategoryController.java

@@ -59,7 +59,7 @@ public class PageCategoryController extends BaseController {
      */
     //@SaCheckPermission("mall:pageCategory:query")
     @GetMapping("/tree")
-    public R<List<PageCategoryVo>> getCategoryTree() {
+    public R<List<PageCategoryVo>> getCategoryTree(String pageType) {
         // 创建一个顶级父节点(id=0,名称为"顶级父类")
         PageCategoryVo rootNode = new PageCategoryVo();
         rootNode.setId(0L);
@@ -70,6 +70,7 @@ public class PageCategoryController extends BaseController {
         // 获取所有分类的扁平列表(不分层)
         PageCategoryBo bo = new PageCategoryBo();
         bo.setStatus(1);
+        bo.setPageType(pageType);
         List<PageCategoryVo> allCategories = pageCategoryService.queryList(bo);
 
         // 将所有parentId=0的分类作为顶级父节点的直接子节点

+ 4 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/bo/PcProductBo.java

@@ -35,6 +35,10 @@ public class PcProductBo {
      * 品牌id
      */
     private String brandIds;
+    /**
+     * 是否自营(1=是,0=否)
+     */
+    private Integer isSelf;
     /**
      * 分类ids(底级)
      * */

+ 45 - 2
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/domain/vo/PcProductVo.java

@@ -23,6 +23,51 @@ public class PcProductVo {
      * 品牌id
      */
     private Long brandId;
+
+    /**
+     * 是否自营(1=是,0=否)
+     */
+    private Integer isSelf;
+    /**
+    * 品牌名称
+    * */
+    private String brandName;
+
+    /**
+     * 顶级分类id
+     */
+    private Long topCategoryId;
+
+    /**
+     * 中级分类id
+     */
+    private Long mediumCategoryId;
+
+    /**
+     * 底层分类id
+     */
+    private Long bottomCategoryId;
+
+    /**
+     * 分类名称(底级分类)
+     */
+    private String categoryName;
+
+    /**
+     * 顶级分类名称
+     */
+    private String topCategoryName;
+
+    /**
+     * 中级分类名称
+     */
+    private String mediumCategoryName;
+
+    /**
+     * 底级分类名称
+     */
+    private String bottomCategoryName;
+
     /**
      * 产品编号
      */
@@ -98,8 +143,6 @@ public class PcProductVo {
     * */
     private String isCollect;
 
-
-
     /**
     * 创建时间
     * */

+ 4 - 0
ruoyi-modules/ruoyi-product/src/main/java/org/dromara/product/service/impl/ProductBaseServiceImpl.java

@@ -498,6 +498,9 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         if (ObjectUtil.isNotEmpty(bo.getCategoryIds())){
             wrapper.in(ProductBaseVo::getBottomCategoryId, bo.getCategoryIds().split(","));
         }
+        if(ObjectUtil.isNotEmpty(bo.getIsSelf())){
+            wrapper.eq(ProductBaseVo::getIsSelf,bo.getIsSelf());
+        }
         // 添加额外的查询条件
         if (ObjectUtil.isNotEmpty(bo.getSearchKeyword())) {
             wrapper.and(e -> e
@@ -1507,6 +1510,7 @@ public class ProductBaseServiceImpl extends ServiceImpl<ProductBaseMapper, Produ
         }
         lqw.ge(ObjectUtil.isNotEmpty(pageQuery.getFirstSeenId()) && pageQuery.getWay() == 0,"b.id", pageQuery.getFirstSeenId());
         lqw.gt(ObjectUtil.isNotEmpty(pageQuery.getLastSeenId()) && pageQuery.getWay() == 1,"b.id", pageQuery.getLastSeenId());
+        lqw.eq(ObjectUtil.isNotEmpty(bo.getIsSelf()), "b.is_self", bo.getIsSelf());
 //        lqw.eq("b.product_status", 1);
         lqw.and(ObjectUtil.isNotEmpty(bo.getSearchKeyword())
             , (queryWrapper) -> queryWrapper.and(qw ->

+ 13 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/pc/IndexSystemController.java

@@ -93,6 +93,19 @@ public class IndexSystemController {
         );
         return R.ok(list);
     }
+    /**
+    * 底部导航
+    * */
+    @GetMapping("/getBottomNav")
+    public R<List<PlatformNavigation>> getBottomNav(@RequestParam(value = "title", required = false) String title) {
+        List<PlatformNavigation> list = platformNavigationService.list(Wrappers.<PlatformNavigation>lambdaQuery(PlatformNavigation.class)
+            .eq(PlatformNavigation::getNavType, "setting_bottom")
+            .eq(StringUtils.isNotBlank(title), PlatformNavigation::getTitle, title)
+            .eq(PlatformNavigation::getIsEnable, SysPlatformYesNo.YES.getCode())
+            .orderByAsc(PlatformNavigation::getSort)
+        );
+        return R.ok(list);
+    }
 
     /**
      * 轮播广告(平台装修-轮播广告)

+ 9 - 3
ruoyi-auth/src/main/java/org/dromara/auth/domain/SysUserWechat.java → ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserWechat.java

@@ -1,4 +1,4 @@
-package org.dromara.auth.domain;
+package org.dromara.system.domain;
 
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
@@ -11,6 +11,8 @@ import java.io.Serial;
 
 /**
  * 小程序用户关联对象 sys_user_wechat
+ *
+ * @author LionLi
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -32,6 +34,10 @@ public class SysUserWechat extends TenantEntity {
      * 微信小程序唯一标识
      */
     private String openId;
+    /**
+    * 公众号openId
+    * */
+    private String gzhOpenId;
 
     /**
      * 微信开放平台唯一标识
@@ -89,8 +95,8 @@ public class SysUserWechat extends TenantEntity {
      */
     private String remark;
 
-    /*
-     *电话号码
+    /**
+     * 电话号码
      */
     private String phoneNumber;
 

+ 109 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserWechatBo.java

@@ -0,0 +1,109 @@
+package org.dromara.system.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.system.domain.SysUserWechat;
+
+/**
+ * 小程序用户关联业务对象 sys_user_wechat
+ *
+ * @author LionLi
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysUserWechat.class, reverseConvertGenerate = false)
+public class SysUserWechatBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    private Long userId;
+
+    /**
+     * 公众号openId
+     * */
+    private String gzhOpenId;
+
+    /**
+     * 微信小程序唯一标识
+     */
+    private String openId;
+
+    /**
+     * 微信开放平台唯一标识
+     */
+    @NotBlank(message = "微信开放平台唯一标识不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String unionId;
+
+    /**
+     * 微信昵称
+     */
+    @NotBlank(message = "微信昵称不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String nickname;
+
+    /**
+     * 头像URL
+     */
+    @NotBlank(message = "头像URL不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String avatarUrl;
+
+    /**
+     * 性别:  0. 男; 1. 女;2. 未知;
+     */
+    @NotNull(message = "性别(0=未知,1=男,2=女)不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Long gender;
+
+    /**
+     * 微信会话密钥(仅后端临时存储,需加密!)
+     */
+    @NotBlank(message = "微信会话密钥(仅后端临时存储,需加密!)不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String sessionKey;
+
+    /**
+     * 微信API临时访问令牌
+     */
+    @NotBlank(message = "微信API临时访问令牌不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String accessToken;
+
+    /**
+     * access_token的有效期(秒)
+     */
+    @NotNull(message = "access_token的有效期(秒)不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Long expireIn;
+
+    /**
+     * 用于刷新access_token的令牌
+     */
+    @NotBlank(message = "用于刷新access_token的令牌不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String refreshToken;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @NotBlank(message = "状态(0正常 1停用)不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String status;
+
+    /**
+     * 备注
+     */
+    @NotBlank(message = "备注不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String remark;
+
+    private String code;
+
+    private String phoneNumber;
+
+    /**
+     * 客户端id
+     */
+    private String clientId;
+
+}

+ 7 - 3
ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/SysUserWechatVo.java → ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserWechatVo.java

@@ -1,18 +1,17 @@
-package org.dromara.auth.domain.vo;
+package org.dromara.system.domain.vo;
 
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
-import org.dromara.auth.domain.SysUserWechat;
 import org.dromara.common.excel.annotation.ExcelDictFormat;
 import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.system.domain.SysUserWechat;
 
 import java.io.Serial;
 import java.io.Serializable;
 import java.util.Date;
 
-
 /**
  * 小程序用户关联视图对象 sys_user_wechat
  *
@@ -40,6 +39,11 @@ public class SysUserWechatVo implements Serializable {
     @ExcelProperty(value = "微信小程序唯一标识")
     private String openId;
 
+    /**
+     * 公众号openId
+     * */
+    private String gzhOpenId;
+
     /**
      * 微信开放平台唯一标识
      */

+ 26 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java

@@ -135,7 +135,10 @@ public class RemoteUserServiceImpl implements RemoteUserService {
     @Override
     public LoginUser getUserInfoByPhonenumber(String phonenumber, String tenantId) throws UserException {
         return TenantHelper.dynamic(tenantId, () -> {
-            SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
+            SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>()
+                .eq(SysUser::getPhonenumber, phonenumber)
+                .in(SysUser::getUserSonType, "3","4")
+            );
             if (ObjectUtil.isNull(sysUser)) {
                 throw new UserException("user.not.exists", phonenumber);
             }
@@ -148,6 +151,28 @@ public class RemoteUserServiceImpl implements RemoteUserService {
         });
     }
 
+    /**
+     * 通过手机号查询用户信息
+     *
+     * @param phonenumber 手机号
+     * @return 结果
+     */
+    @Override
+    public LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException {
+        return TenantHelper.dynamic("000000", () -> {
+            SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>()
+                .eq(SysUser::getPhonenumber, phonenumber)
+                .in(SysUser::getUserSonType, "3","4")
+            );
+            if (ObjectUtil.isNull(sysUser)) {
+                return null;
+            }
+            // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
+            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
+            return buildLoginUser(sysUser);
+        });
+    }
+
     /**
      * 通过邮箱查询用户信息
      *

+ 110 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserWechatServiceImpl.java

@@ -0,0 +1,110 @@
+package org.dromara.system.dubbo;
+
+import cn.hutool.core.bean.BeanUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.system.api.RemoteUserWechatService;
+import org.dromara.system.api.domain.vo.RemoteUserWechatVo;
+import org.dromara.system.domain.SysUserWechat;
+import org.dromara.system.domain.bo.SysUserWechatBo;
+import org.dromara.system.domain.vo.SysUserWechatVo;
+import org.dromara.system.service.ISysUserWechatService;
+import org.springframework.stereotype.Service;
+
+import java.util.Objects;
+
+/**
+ * 小程序用户关联服务实现
+ *
+ * @author LionLi
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+@DubboService
+public class RemoteUserWechatServiceImpl implements RemoteUserWechatService {
+
+    private final ISysUserWechatService userWechatService;
+
+    /**
+     * 通过openid查询小程序用户信息
+     *
+     * @param openId 微信openid
+     * @return 结果
+     */
+    @Override
+    public RemoteUserWechatVo getUserInfoByOpenid(String openId) {
+        SysUserWechat userWechat = userWechatService.queryByOpenId(openId);
+        if (userWechat == null) {
+            return null;
+        }
+        return BeanUtil.copyProperties(userWechat, RemoteUserWechatVo.class);
+    }
+
+    /**
+     * 通过手机号查询小程序用户信息
+     *
+     * @param phoneNumber 手机号
+     * @return 结果
+     */
+    @Override
+    public RemoteUserWechatVo getUserInfoByPhoneNumber(String phoneNumber) {
+        SysUserWechat userWechat = userWechatService.queryByPhoneNumber(phoneNumber);
+        if (userWechat == null) {
+            return null;
+        }
+        return BeanUtil.copyProperties(userWechat, RemoteUserWechatVo.class);
+    }
+
+    /**
+     * 新增小程序用户关联
+     *
+     * @param userWechat 小程序用户关联信息
+     * @return 结果
+     */
+    @Override
+    public Boolean insertUserWechat(RemoteUserWechatVo userWechat) {
+        try {
+            SysUserWechatBo bo = BeanUtil.copyProperties(userWechat, SysUserWechatBo.class);
+            return userWechatService.insertByBo(bo);
+        } catch (Exception e) {
+            log.error("新增小程序用户关联失败: openId={}, phoneNumber={}",
+                userWechat.getOpenId(), userWechat.getPhoneNumber(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * 修改小程序用户关联
+     *
+     * @param userWechat 小程序用户关联信息
+     * @return 结果
+     */
+    @Override
+    public Boolean updateUserWechat(RemoteUserWechatVo userWechat) {
+        try {
+            SysUserWechatBo bo = BeanUtil.copyProperties(userWechat, SysUserWechatBo.class);
+            return userWechatService.updateByBo(bo);
+        } catch (Exception e) {
+            log.error("修改小程序用户关联失败: id={}", userWechat.getId(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * 通过ID查询小程序用户信息
+     *
+     * @param id 主键
+     * @return 结果
+     */
+    @Override
+    public RemoteUserWechatVo getUserInfoById(Long id) {
+        SysUserWechatVo vo = userWechatService.queryById(id);
+        if (vo == null) {
+            return null;
+        }
+        return BeanUtil.copyProperties(vo, RemoteUserWechatVo.class);
+    }
+}

+ 3 - 4
ruoyi-auth/src/main/java/org/dromara/auth/mapper/SysUserWechatMapper.java → ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserWechatMapper.java

@@ -1,14 +1,13 @@
-package org.dromara.auth.mapper;
+package org.dromara.system.mapper;
 
-import org.dromara.auth.domain.SysUserWechat;
-import org.dromara.auth.domain.vo.SysUserWechatVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.system.domain.SysUserWechat;
+import org.dromara.system.domain.vo.SysUserWechatVo;
 
 /**
  * 小程序用户关联Mapper接口
  *
  * @author LionLi
- * @date 2026-04-22
  */
 public interface SysUserWechatMapper extends BaseMapperPlus<SysUserWechat, SysUserWechatVo> {
 

+ 62 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserWechatService.java

@@ -0,0 +1,62 @@
+package org.dromara.system.service;
+
+import org.dromara.system.domain.SysUserWechat;
+import org.dromara.system.domain.bo.SysUserWechatBo;
+import org.dromara.system.domain.vo.SysUserWechatVo;
+
+/**
+ * 小程序用户关联Service接口
+ *
+ * @author LionLi
+ */
+public interface ISysUserWechatService {
+
+    /**
+     * 查询小程序用户关联
+     *
+     * @param id 主键
+     * @return 小程序用户关联
+     */
+    SysUserWechatVo queryById(Long id);
+
+    /**
+     * 通过openid查询小程序用户
+     *
+     * @param openId 微信openid
+     * @return 小程序用户关联
+     */
+    SysUserWechat queryByOpenId(String openId);
+
+    /**
+     * 通过手机号查询小程序用户
+     *
+     * @param phoneNumber 手机号
+     * @return 小程序用户关联
+     */
+    SysUserWechat queryByPhoneNumber(String phoneNumber);
+
+    /**
+     * 新增小程序用户关联
+     *
+     * @param bo 小程序用户关联
+     * @return 结果
+     */
+    Boolean insertByBo(SysUserWechatBo bo);
+
+    /**
+     * 修改小程序用户关联
+     *
+     * @param bo 小程序用户关联
+     * @return 结果
+     */
+    Boolean updateByBo(SysUserWechatBo bo);
+
+    /**
+     * 校验并批量删除小程序用户关联信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 结果
+     */
+    Boolean deleteWithValidByIds(java.util.Collection<Long> ids, Boolean isValid);
+}

+ 117 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserWechatServiceImpl.java

@@ -0,0 +1,117 @@
+package org.dromara.system.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.system.domain.SysUserWechat;
+import org.dromara.system.domain.bo.SysUserWechatBo;
+import org.dromara.system.domain.vo.SysUserWechatVo;
+import org.dromara.system.mapper.SysUserWechatMapper;
+import org.dromara.system.service.ISysUserWechatService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+
+/**
+ * 小程序用户关联Service业务层处理
+ *
+ * @author LionLi
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class SysUserWechatServiceImpl implements ISysUserWechatService {
+
+    private final SysUserWechatMapper baseMapper;
+
+    /**
+     * 查询小程序用户关联
+     *
+     * @param id 主键
+     * @return 小程序用户关联
+     */
+    @Override
+    public SysUserWechatVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 通过openid查询小程序用户
+     *
+     * @param openId 微信openid
+     * @return 小程序用户关联
+     */
+    @Override
+    public SysUserWechat queryByOpenId(String openId) {
+        LambdaQueryWrapper<SysUserWechat> lqw =
+            new LambdaQueryWrapper<SysUserWechat>().eq(SysUserWechat::getOpenId, openId);
+        return baseMapper.selectOne(lqw);
+    }
+
+    /**
+     * 通过手机号查询小程序用户
+     *
+     * @param phoneNumber 手机号
+     * @return 小程序用户关联
+     */
+    @Override
+    public SysUserWechat queryByPhoneNumber(String phoneNumber) {
+        LambdaQueryWrapper<SysUserWechat> lqw =
+            new LambdaQueryWrapper<SysUserWechat>().eq(SysUserWechat::getPhoneNumber, phoneNumber);
+        return baseMapper.selectOne(lqw);
+    }
+
+    /**
+     * 新增小程序用户关联
+     *
+     * @param bo 小程序用户关联
+     * @return 结果
+     */
+    @Override
+    public Boolean insertByBo(SysUserWechatBo bo) {
+        SysUserWechat add = MapstructUtils.convert(bo, SysUserWechat.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改小程序用户关联
+     *
+     * @param bo 小程序用户关联
+     * @return 结果
+     */
+    @Override
+    public Boolean updateByBo(SysUserWechatBo bo) {
+        SysUserWechat update = MapstructUtils.convert(bo, SysUserWechat.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 校验并批量删除小程序用户关联信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 结果
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            // TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(SysUserWechat entity) {
+        // TODO 做一些数据校验,如唯一约束
+    }
+}