Răsfoiți Sursa

扩展mybatis-plus支持replace等操作
公众号用户与小程序用户关联

skyline 1 an în urmă
părinte
comite
4be97cfe8a
33 a modificat fișierele cu 759 adăugiri și 114 ștergeri
  1. 0 7
      admin/pom.xml
  2. 39 0
      admin/src/main/java/com/kym/admin/config/WxMpConfig.java
  3. 12 0
      common/pom.xml
  4. 55 0
      entity/src/main/java/com/kym/entity/miniapp/MpRelation.java
  5. 2 2
      mapper/src/main/java/com/kym/mapper/admin/ActivityMapper.java
  6. 2 2
      mapper/src/main/java/com/kym/mapper/admin/ConnectorInfoMapper.java
  7. 2 2
      mapper/src/main/java/com/kym/mapper/admin/EquipmentInfoMapper.java
  8. 2 2
      mapper/src/main/java/com/kym/mapper/admin/StationMapper.java
  9. 16 0
      mapper/src/main/java/com/kym/mapper/miniapp/MpRelationMapper.java
  10. 32 0
      mapper/src/main/java/com/kym/mapper/mybatisplus/MyBaseMapper.java
  11. 23 0
      mapper/src/main/resources/mappers/miniapp/MpRelationMapper.xml
  12. 0 7
      miniapp/pom.xml
  13. 24 24
      miniapp/src/main/java/com/kym/miniapp/config/WxMpConfig.java
  14. 18 0
      miniapp/src/main/java/com/kym/miniapp/controller/MpRelationController.java
  15. 92 0
      miniapp/src/main/java/com/kym/miniapp/controller/TestController.java
  16. 0 6
      service/pom.xml
  17. 0 19
      service/src/main/java/com/kym/service/MyBaseService.java
  18. 0 32
      service/src/main/java/com/kym/service/MyBaseServiceImpl.java
  19. 1 1
      service/src/main/java/com/kym/service/admin/ConnectorInfoService.java
  20. 1 1
      service/src/main/java/com/kym/service/admin/EquipmentInfoService.java
  21. 1 1
      service/src/main/java/com/kym/service/admin/impl/ActivityServiceImpl.java
  22. 1 1
      service/src/main/java/com/kym/service/admin/impl/ConnectorInfoServiceImpl.java
  23. 1 6
      service/src/main/java/com/kym/service/admin/impl/EquipmentInfoServiceImpl.java
  24. 1 1
      service/src/main/java/com/kym/service/admin/impl/StationServiceImpl.java
  25. 19 0
      service/src/main/java/com/kym/service/miniapp/MpRelationService.java
  26. 96 0
      service/src/main/java/com/kym/service/miniapp/impl/MpRelationServiceImpl.java
  27. 27 0
      service/src/main/java/com/kym/service/mybatisplus/CustomerSqlInjector.java
  28. 40 0
      service/src/main/java/com/kym/service/mybatisplus/MyBaseService.java
  29. 82 0
      service/src/main/java/com/kym/service/mybatisplus/MyBaseServiceImpl.java
  30. 27 0
      service/src/main/java/com/kym/service/mybatisplus/MySqlMethod.java
  31. 33 0
      service/src/main/java/com/kym/service/mybatisplus/MybatisPlusConfig.java
  32. 61 0
      service/src/main/java/com/kym/service/mybatisplus/injector/method/InsertIgnore.java
  33. 49 0
      service/src/main/java/com/kym/service/mybatisplus/injector/method/Replace.java

+ 0 - 7
admin/pom.xml

@@ -49,13 +49,6 @@
             <version>1.6.2</version>
         </dependency>
 
-<!--运营看板小程序-->
-        <dependency>
-            <groupId>com.github.binarywang</groupId>
-            <artifactId>weixin-java-miniapp</artifactId>
-            <version>4.5.0</version>
-        </dependency>
-
     </dependencies>
 
     <properties>

+ 39 - 0
admin/src/main/java/com/kym/admin/config/WxMpConfig.java

@@ -0,0 +1,39 @@
+package com.kym.admin.config;
+
+import lombok.Data;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author skyline
+ * @description 微信
+ * @date 2023-07-22 23:09
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "wechat.mp")
+public class WxMpConfig {
+    private String appid;
+    private String secret;
+    private String token;
+    private String aeskey;
+
+
+    @Bean
+    public WxMpService wxMpService() {
+        WxMpService service = new WxMpServiceImpl();
+        WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
+
+        configStorage.setAppId(appid);
+        configStorage.setSecret(secret);
+        configStorage.setToken(token);
+        configStorage.setAesKey("aeskey");
+        service.addConfigStorage("MP", configStorage);
+        return service;
+    }
+
+}

+ 12 - 0
common/pom.xml

@@ -118,6 +118,18 @@
             <version>1.1.4</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-miniapp</artifactId>
+            <version>4.6.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-mp</artifactId>
+            <version>4.6.0</version>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 55 - 0
entity/src/main/java/com/kym/entity/miniapp/MpRelation.java

@@ -0,0 +1,55 @@
+package com.kym.entity.miniapp;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.kym.entity.BaseEntity;
+import java.io.Serializable;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 微信公众号用户关联表
+ * </p>
+ *
+ * @author skyline
+ * @since 2024-07-31
+ */
+@Getter
+@Setter
+@TableName("t_mp_relation")
+@Accessors(chain = true)
+public class MpRelation extends BaseEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户id
+     */
+    private Long userId;
+
+    /**
+     * 用户在小程序的openid
+     */
+    private String openid;
+
+    /**
+     * 用户在公众号的openid
+     */
+    private String mpOpenid;
+
+    /**
+     * 用户unionid
+     */
+    private String unionid;
+
+    /**
+     * 用户是否订阅该公众号标识
+     */
+    private Boolean subscribe;
+
+    /**
+     * 渠道来源:ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENE_PROFILE_LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_WECHAT_ADVERTISEMENT 微信广告,ADD_SCENE_REPRINT 他人转载,ADD_SCENE_LIVESTREAM 视频号直播,ADD_SCENE_CHANNELS 视频号,ADD_SCENE_WXA 小程序关注,ADD_SCENE_OTHERS 其他
+     */
+    private String subscribeScene;
+}

+ 2 - 2
mapper/src/main/java/com/kym/mapper/admin/ActivityMapper.java

@@ -1,7 +1,7 @@
 package com.kym.mapper.admin;
 
-import com.github.yulichang.base.MPJBaseMapper;
 import com.kym.entity.admin.Activity;
+import com.kym.mapper.mybatisplus.MyBaseMapper;
 
 /**
  * <p>
@@ -11,6 +11,6 @@ import com.kym.entity.admin.Activity;
  * @author skyline
  * @since 2023-10-18
  */
-public interface ActivityMapper extends MPJBaseMapper<Activity> {
+public interface ActivityMapper extends MyBaseMapper<Activity> {
 
 }

+ 2 - 2
mapper/src/main/java/com/kym/mapper/admin/ConnectorInfoMapper.java

@@ -1,7 +1,7 @@
 package com.kym.mapper.admin;
 
-import com.github.yulichang.base.MPJBaseMapper;
 import com.kym.entity.admin.ConnectorInfo;
+import com.kym.mapper.mybatisplus.MyBaseMapper;
 
 /**
  * <p>
@@ -11,6 +11,6 @@ import com.kym.entity.admin.ConnectorInfo;
  * @author skyline
  * @since 2023-08-05
  */
-public interface ConnectorInfoMapper extends MPJBaseMapper<ConnectorInfo> {
+public interface ConnectorInfoMapper extends MyBaseMapper<ConnectorInfo> {
 
 }

+ 2 - 2
mapper/src/main/java/com/kym/mapper/admin/EquipmentInfoMapper.java

@@ -1,7 +1,7 @@
 package com.kym.mapper.admin;
 
-import com.github.yulichang.base.MPJBaseMapper;
 import com.kym.entity.admin.EquipmentInfo;
+import com.kym.mapper.mybatisplus.MyBaseMapper;
 
 /**
  * <p>
@@ -11,6 +11,6 @@ import com.kym.entity.admin.EquipmentInfo;
  * @author skyline
  * @since 2023-08-05
  */
-public interface EquipmentInfoMapper extends MPJBaseMapper<EquipmentInfo> {
+public interface EquipmentInfoMapper extends MyBaseMapper<EquipmentInfo> {
 
 }

+ 2 - 2
mapper/src/main/java/com/kym/mapper/admin/StationMapper.java

@@ -1,7 +1,7 @@
 package com.kym.mapper.admin;
 
-import com.github.yulichang.base.MPJBaseMapper;
 import com.kym.entity.admin.Station;
+import com.kym.mapper.mybatisplus.MyBaseMapper;
 
 /**
  * <p>
@@ -11,6 +11,6 @@ import com.kym.entity.admin.Station;
  * @author skyline
  * @since 2023-08-12
  */
-public interface StationMapper extends MPJBaseMapper<Station> {
+public interface StationMapper extends MyBaseMapper<Station> {
 
 }

+ 16 - 0
mapper/src/main/java/com/kym/mapper/miniapp/MpRelationMapper.java

@@ -0,0 +1,16 @@
+package com.kym.mapper.miniapp;
+
+import com.kym.entity.miniapp.MpRelation;
+import com.kym.mapper.mybatisplus.MyBaseMapper;
+
+/**
+ * <p>
+ * 微信公众号用户关联表 Mapper 接口
+ * </p>
+ *
+ * @author skyline
+ * @since 2024-07-31
+ */
+public interface MpRelationMapper extends MyBaseMapper<MpRelation> {
+
+}

+ 32 - 0
mapper/src/main/java/com/kym/mapper/mybatisplus/MyBaseMapper.java

@@ -0,0 +1,32 @@
+package com.kym.mapper.mybatisplus;
+
+import com.github.yulichang.base.MPJBaseMapper;
+
+import java.util.Collection;
+
+/**
+ * 自定义mapper 扩展Mybatis-Plus
+ *
+ * @author skyline
+ */
+public interface MyBaseMapper<T> extends MPJBaseMapper<T> {
+    /**
+     * 插入数据,如果中已经存在相同的记录,则忽略当前新数据
+     *
+     * @param entity 实体类
+     * @return 影响条数
+     */
+    int insertIgnore(T entity);
+
+
+    /**
+     * 替换数据
+     * replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引,如果数据库已经存在数据,则用新数据替换,如果没有数据效果则和insert into一样;
+     *
+     * @param entity 实体类
+     * @return 影响条数
+     */
+    int replace(T entity);
+
+    int batchReplace(Collection<T> entityList);
+}

+ 23 - 0
mapper/src/main/resources/mappers/miniapp/MpRelationMapper.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.kym.mapper.miniapp.MpRelationMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.kym.entity.miniapp.MpRelation">
+        <result column="id" property="id" />
+        <result column="user_id" property="userId" />
+        <result column="openid" property="openid" />
+        <result column="mp_openid" property="mpOpenid" />
+        <result column="unionid" property="unionid" />
+        <result column="subscribe" property="subscribe" />
+        <result column="subscribe_scene" property="subscribeScene" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id,user_id, openid, mp_openid, unionid, subscribe, subscribe_scene,create_time,update_time
+    </sql>
+
+</mapper>

+ 0 - 7
miniapp/pom.xml

@@ -49,13 +49,6 @@
             <version>3.1.1</version>
         </dependency>
 
-
-        <dependency>
-            <groupId>com.github.binarywang</groupId>
-            <artifactId>weixin-java-miniapp</artifactId>
-            <version>4.5.0</version>
-        </dependency>
-
         <dependency>
             <groupId>com.sun.mail</groupId>
             <artifactId>javax.mail</artifactId>

+ 24 - 24
miniapp/src/main/java/com/kym/miniapp/config/WxMpConfig.java

@@ -13,27 +13,27 @@ import org.springframework.context.annotation.Configuration;
  * @description 微信
  * @date 2023-07-22 23:09
  */
-//@Data
-//@Configuration
-//@ConfigurationProperties(prefix = "wechat.mp")
-//public class WxMpConfig {
-//    private String appid;
-//    private String secret;
-//    private String token;
-//    private String aeskey;
-//
-//
-//    @Bean
-//    public WxMpService wxMpService() {
-//        WxMpService service = new WxMpServiceImpl();
-//        WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
-//
-//        configStorage.setAppId(appid);
-//        configStorage.setSecret(secret);
-//        configStorage.setToken(token);
-//        configStorage.setAesKey("aeskey");
-//        service.addConfigStorage("MP", configStorage);
-//        return service;
-//    }
-//
-//}
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "wechat.mp")
+public class WxMpConfig {
+    private String appid;
+    private String secret;
+    private String token;
+    private String aeskey;
+
+
+    @Bean
+    public WxMpService wxMpService() {
+        WxMpService service = new WxMpServiceImpl();
+        WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
+
+        configStorage.setAppId(appid);
+        configStorage.setSecret(secret);
+        configStorage.setToken(token);
+        configStorage.setAesKey("aeskey");
+        service.addConfigStorage("MP", configStorage);
+        return service;
+    }
+
+}

+ 18 - 0
miniapp/src/main/java/com/kym/miniapp/controller/MpRelationController.java

@@ -0,0 +1,18 @@
+package com.kym.miniapp.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 微信公众号用户关联表 前端控制器
+ * </p>
+ *
+ * @author skyline
+ * @since 2024-07-31
+ */
+@RestController
+@RequestMapping("/mp-relation")
+public class MpRelationController {
+
+}

+ 92 - 0
miniapp/src/main/java/com/kym/miniapp/controller/TestController.java

@@ -0,0 +1,92 @@
+package com.kym.admin.controller;
+
+import com.kym.common.R;
+import com.kym.common.utils.wx.WxPbUtil;
+import com.kym.entity.miniapp.UserCoupon;
+import com.kym.service.mq.producer.UserCouponSender;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import me.chanjar.weixin.mp.bean.result.WxMpUserList;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+@RestController
+@RequestMapping("/test")
+public class TestController {
+
+
+    private final UserCouponSender userCouponSender;
+
+    private final WxMpService wxMpService;
+
+    public TestController(UserCouponSender userCouponSender, WxMpService wxMpService) {
+
+        this.userCouponSender = userCouponSender;
+        this.wxMpService = wxMpService;
+    }
+
+    /**
+     * 测试rabbitmq
+     *
+     * @return
+     */
+    @GetMapping(value = "/test")
+    R<?> test() {
+        for (int i = 0; i < 10; i++) {
+            var userCoupon = new UserCoupon().setUserId(i + 1L);
+            userCouponSender.sendMessage(userCoupon);
+        }
+        return R.success();
+    }
+
+    /**
+     * 测试公众号消息
+     *
+     * @return
+     */
+    @GetMapping(value = "/test2")
+    R<?> test2() {
+        // 通过unionid获取公众号openid
+
+        var params = Map.of(
+                "character_string2.DATA", "order111222333",
+                "thing9.DATA", "测试站点",
+                "character_string3.DATA", "SN0001",
+                "time4.DATA", "2024-07-29 12:00:00"
+        );
+        WxPbUtil.sendPublicTemplateMessage("", "SjszYYiHcN-GaEeZTKJsCcB6ACxYJWfTddkSywf1q1g", params, "", "");
+        return R.success();
+    }
+
+    /**
+     * 测试拉取公众号关注者列表
+     * @return
+     */
+    @GetMapping(value = "/test3")
+    R<?> test3() throws WxErrorException {
+        // 获取公众号所有关注者列表
+        WxMpUserList wxUserList = wxMpService.getUserService().userList();
+        // 通过unionid获取公众号openid
+        String lang = "zh_CN";
+        var unionid2openid = new HashMap<>();
+        wxUserList.getOpenids().forEach(openid -> {
+            try {
+                WxMpUser mpUser = wxMpService.getUserService().userInfo(openid,lang);
+                unionid2openid.put(mpUser.getUnionId(),openid);
+            } catch (WxErrorException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+
+        return R.success(wxUserList);
+    }
+
+
+}

+ 0 - 6
service/pom.xml

@@ -37,12 +37,6 @@
             <version>0.2.11</version>
         </dependency>
 
-        <dependency>
-            <groupId>com.github.binarywang</groupId>
-            <artifactId>weixin-java-mp</artifactId>
-            <version>4.5.0</version>
-        </dependency>
-
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-amqp</artifactId>

+ 0 - 19
service/src/main/java/com/kym/service/MyBaseService.java

@@ -1,19 +0,0 @@
-package com.kym.service;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.github.yulichang.base.MPJBaseService;
-
-import java.util.Collection;
-import java.util.function.Function;
-
-
-/**
- * 扩展mybatis-plus 实现指定字段更新
- *
- * @param <T>
- */
-public interface MyBaseService<T> extends MPJBaseService<T> {
-    public boolean updateBatchByQueryWrapper(Collection<T> entityList, Function<T, QueryWrapper<T>> queryWrapperFunction);
-
-    public boolean updateByQueryWrapper(T entity, Function<T, QueryWrapper<T>> queryWrapperFunction);
-}

+ 0 - 32
service/src/main/java/com/kym/service/MyBaseServiceImpl.java

@@ -1,32 +0,0 @@
-package com.kym.service;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.enums.SqlMethod;
-import com.baomidou.mybatisplus.core.toolkit.Constants;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
-import com.github.yulichang.base.MPJBaseMapper;
-import com.github.yulichang.base.MPJBaseService;
-import org.apache.ibatis.binding.MapperMethod;
-
-import java.util.Collection;
-import java.util.function.Function;
-
-public class MyBaseServiceImpl<M extends MPJBaseMapper<T>, T> extends ServiceImpl<M, T> implements MPJBaseService<T>, MyBaseService<T> {
-
-    @Override
-    public boolean updateBatchByQueryWrapper(Collection<T> entityList, Function<T, QueryWrapper<T>> queryWrapperFunction) {
-        String sqlStatement = this.getSqlStatement(SqlMethod.UPDATE);
-        return this.executeBatch(entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {
-            MapperMethod.ParamMap param = new MapperMethod.ParamMap();
-            param.put(Constants.ENTITY, entity);
-            param.put(Constants.WRAPPER, queryWrapperFunction.apply(entity));
-            sqlSession.update(sqlStatement, param);
-        });
-    }
-
-    @Override
-    public boolean updateByQueryWrapper(T entity, Function<T, QueryWrapper<T>> queryWrapperFunction) {
-        return SqlHelper.retBool(getBaseMapper().update(entity, queryWrapperFunction.apply(entity)));
-    }
-}

+ 1 - 1
service/src/main/java/com/kym/service/admin/ConnectorInfoService.java

@@ -4,7 +4,7 @@ import com.kym.entity.admin.ConnectorInfo;
 import com.kym.entity.admin.queryParams.EquipmentQueryParam;
 import com.kym.entity.admin.vo.ConnectorInfoVo;
 import com.kym.entity.common.PageBean;
-import com.kym.service.MyBaseService;
+import com.kym.service.mybatisplus.MyBaseService;
 
 import java.util.Map;
 

+ 1 - 1
service/src/main/java/com/kym/service/admin/EquipmentInfoService.java

@@ -1,7 +1,7 @@
 package com.kym.service.admin;
 
 import com.kym.entity.admin.EquipmentInfo;
-import com.kym.service.MyBaseService;
+import com.kym.service.mybatisplus.MyBaseService;
 
 /**
  * <p>

+ 1 - 1
service/src/main/java/com/kym/service/admin/impl/ActivityServiceImpl.java

@@ -14,7 +14,7 @@ import com.kym.entity.common.PageBean;
 import com.kym.entity.common.RedisKeys;
 import com.kym.entity.miniapp.UserRechargeRights;
 import com.kym.mapper.admin.ActivityMapper;
-import com.kym.service.MyBaseServiceImpl;
+import com.kym.service.mybatisplus.MyBaseServiceImpl;
 import com.kym.service.admin.*;
 import com.kym.service.jobs.DelayService;
 import com.kym.service.miniapp.UserRechargeRightsService;

+ 1 - 1
service/src/main/java/com/kym/service/admin/impl/ConnectorInfoServiceImpl.java

@@ -12,10 +12,10 @@ import com.kym.entity.admin.vo.ConnectorInfoVo;
 import com.kym.entity.common.PageBean;
 import com.kym.entity.miniapp.ChargeOrder;
 import com.kym.mapper.admin.ConnectorInfoMapper;
-import com.kym.service.MyBaseServiceImpl;
 import com.kym.service.admin.ConnectorInfoService;
 import com.kym.service.cache.KymCache;
 import com.kym.service.miniapp.ChargeOrderService;
+import com.kym.service.mybatisplus.MyBaseServiceImpl;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 

+ 1 - 6
service/src/main/java/com/kym/service/admin/impl/EquipmentInfoServiceImpl.java

@@ -1,17 +1,12 @@
 package com.kym.service.admin.impl;
 
 import com.baomidou.dynamic.datasource.annotation.DS;
-import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
 import com.kym.entity.admin.EquipmentInfo;
 import com.kym.mapper.admin.EquipmentInfoMapper;
-import com.kym.service.MyBaseServiceImpl;
+import com.kym.service.mybatisplus.MyBaseServiceImpl;
 import com.kym.service.admin.EquipmentInfoService;
-import com.kym.service.cache.KymCache;
-import jakarta.annotation.PostConstruct;
 import org.springframework.stereotype.Service;
 
-import java.util.stream.Collectors;
-
 /**
  * <p>
  * 充电桩桩体设备 服务实现类

+ 1 - 1
service/src/main/java/com/kym/service/admin/impl/StationServiceImpl.java

@@ -18,7 +18,7 @@ import com.kym.entity.admin.vo.StationVo;
 import com.kym.entity.enplus.EnStationStatsInfo;
 import com.kym.entity.enplus.EnStationStatusInfo;
 import com.kym.mapper.admin.StationMapper;
-import com.kym.service.MyBaseServiceImpl;
+import com.kym.service.mybatisplus.MyBaseServiceImpl;
 import com.kym.service.admin.*;
 import com.kym.service.cache.KymCache;
 import com.kym.service.enplus.EnPlusService;

+ 19 - 0
service/src/main/java/com/kym/service/miniapp/MpRelationService.java

@@ -0,0 +1,19 @@
+package com.kym.service.miniapp;
+
+import com.kym.entity.miniapp.MpRelation;
+import com.kym.service.mybatisplus.MyBaseService;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * <p>
+ * 微信公众号用户关联表 服务类
+ * </p>
+ *
+ * @author skyline
+ * @since 2024-07-31
+ */
+public interface MpRelationService extends MyBaseService<MpRelation> {
+
+    // 关联小程序用户
+    void bindMpUser() throws WxErrorException;
+}

+ 96 - 0
service/src/main/java/com/kym/service/miniapp/impl/MpRelationServiceImpl.java

@@ -0,0 +1,96 @@
+package com.kym.service.miniapp.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.kym.common.utils.CommUtil;
+import com.kym.common.utils.IDGenerator;
+import com.kym.entity.miniapp.MpRelation;
+import com.kym.entity.miniapp.User;
+import com.kym.mapper.miniapp.MpRelationMapper;
+import com.kym.service.miniapp.MpRelationService;
+import com.kym.service.miniapp.UserService;
+import com.kym.service.mybatisplus.MyBaseServiceImpl;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import me.chanjar.weixin.mp.bean.result.WxMpUserList;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+
+/**
+ * <p>
+ * 微信公众号用户关联表 服务实现类
+ * </p>
+ *
+ * @author skyline
+ * @since 2024-07-31
+ */
+@Service
+public class MpRelationServiceImpl extends MyBaseServiceImpl<MpRelationMapper, MpRelation> implements MpRelationService {
+
+    private final UserService userService;
+    private final WxMpService wxMpService;
+
+
+    public MpRelationServiceImpl(UserService userService, WxMpService wxMpService) {
+        this.userService = userService;
+        this.wxMpService = wxMpService;
+    }
+
+
+    /**
+     * 公众号用户关联小程序用户
+     *
+     * @throws WxErrorException
+     */
+    @Override
+    @Transactional
+    public void bindMpUser() throws WxErrorException {
+        String nextOpenid = null;
+        for (; ; ) {
+            // 获取公众号所有关注者列表
+            WxMpUserList wxUserList = wxMpService.getUserService().userList(nextOpenid);
+            if (CommUtil.isEmptyOrNull(wxUserList.getNextOpenid())) {
+                return;
+            } else {
+                nextOpenid = wxUserList.getNextOpenid();
+            }
+            // 通过unionid获取公众号openid
+            String lang = "zh_CN";
+            var mpRelationList = new ArrayList<MpRelation>();
+            wxUserList.getOpenids().forEach(openid -> {
+                try {
+                    WxMpUser mpUser = wxMpService.getUserService().userInfo(openid, lang);
+                    MpRelation mpRelation = new MpRelation();
+                    mpRelation.setId(IDGenerator.INS().nextId());
+                    mpRelation
+                            .setMpOpenid(mpUser.getOpenId())
+                            .setUnionid(mpUser.getUnionId())
+                            .setSubscribe(mpUser.getSubscribe())
+                            .setSubscribeScene(mpUser.getSubscribeScene());
+                    mpRelationList.add(mpRelation);
+                } catch (WxErrorException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+
+            // todo 优化成批量replace
+            // saveBatch(mpRelationList);
+
+            replaceBatch(mpRelationList);
+
+            var mpRelations = mpRelationList.stream().filter(mpRelation -> mpRelation.getUnionid() != null && !mpRelation.getUnionid().isEmpty()).toList();
+            var userList = userService.lambdaQuery().in(User::getUnionid, mpRelations.stream().map(MpRelation::getUnionid).toList()).list();
+            userList.forEach(user -> {
+                mpRelations.stream().filter(mpRelation -> mpRelation.getUnionid().equals(user.getUnionid())).findFirst().ifPresent(mpRelation -> {
+                    mpRelation.setOpenid(user.getOpenid());
+                    mpRelation.setUserId(user.getId());
+                });
+            });
+            updateBatchByQueryWrapper(mpRelations, mpRelation ->
+                    new QueryWrapper<MpRelation>().eq("unionid", mpRelation.getUnionid()));
+        }
+    }
+
+}

+ 27 - 0
service/src/main/java/com/kym/service/mybatisplus/CustomerSqlInjector.java

@@ -0,0 +1,27 @@
+package com.kym.service.mybatisplus;
+
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.kym.service.mybatisplus.injector.method.InsertIgnore;
+import com.kym.service.mybatisplus.injector.method.Replace;
+
+import java.util.List;
+
+/**
+ * 自定义sql注入器,增加通用方法
+ *
+ * @author chqiu
+ */
+public class CustomerSqlInjector extends DefaultSqlInjector {
+
+    @Override
+    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
+        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
+        // 插入数据,如果中已经存在相同的记录,则忽略当前新数据
+        methodList.add(new InsertIgnore());
+        // 替换数据,如果中已经存在相同的记录,则覆盖旧数据
+        methodList.add(new Replace());
+        return methodList;
+    }
+}

+ 40 - 0
service/src/main/java/com/kym/service/mybatisplus/MyBaseService.java

@@ -0,0 +1,40 @@
+package com.kym.service.mybatisplus;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.github.yulichang.base.MPJBaseService;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.function.Function;
+
+
+/**
+ * 扩展mybatis-plus 实现指定字段更新
+ *
+ * @param <T>
+ */
+public interface MyBaseService<T> extends MPJBaseService<T> {
+    public boolean updateBatchByQueryWrapper(Collection<T> entityList, Function<T, QueryWrapper<T>> queryWrapperFunction);
+
+    public boolean updateByQueryWrapper(T entity, Function<T, QueryWrapper<T>> queryWrapperFunction);
+
+    /**
+     * 插入数据,如果中已经存在相同的记录,则忽略当前新数据
+     */
+    int insertIgnore(T entity);
+
+    /**
+     * 替换数据
+     * replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引,如果数据库已经存在数据,则用新数据替换,如果没有数据效果则和insert into一样;
+     */
+    int replace(T entity);
+
+
+    /**
+     * 批量替换数据
+     *
+     * @param entityList
+     * @return
+     */
+    boolean replaceBatch(Collection<T> entityList);
+}

+ 82 - 0
service/src/main/java/com/kym/service/mybatisplus/MyBaseServiceImpl.java

@@ -0,0 +1,82 @@
+package com.kym.service.mybatisplus;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.enums.SqlMethod;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import com.github.yulichang.base.MPJBaseService;
+import com.kym.mapper.mybatisplus.MyBaseMapper;
+import org.apache.ibatis.binding.MapperMethod;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.function.Function;
+
+/**
+ * 扩展mybatis-plus,支持按照指定字段更新单挑或批量数据
+ *
+ * @author skyline
+ */
+public class MyBaseServiceImpl<M extends MyBaseMapper<T>, T> extends ServiceImpl<M, T> implements MPJBaseService<T>, MyBaseService<T> {
+
+
+
+    /**
+     * 根据指定字段批量更新
+     *
+     * @param entityList
+     * @param queryWrapperFunction
+     * @return
+     */
+    @Override
+    public boolean updateBatchByQueryWrapper(Collection<T> entityList, Function<T, QueryWrapper<T>> queryWrapperFunction) {
+        String sqlStatement = this.getSqlStatement(SqlMethod.UPDATE);
+        return this.executeBatch(entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {
+            MapperMethod.ParamMap param = new MapperMethod.ParamMap<>();
+            param.put(Constants.ENTITY, entity);
+            param.put(Constants.WRAPPER, queryWrapperFunction.apply(entity));
+            sqlSession.update(sqlStatement, param);
+        });
+    }
+
+    /**
+     * 根据指定字段更新单条数据
+     *
+     * @param entity
+     * @param queryWrapperFunction
+     * @return
+     */
+    @Override
+    public boolean updateByQueryWrapper(T entity, Function<T, QueryWrapper<T>> queryWrapperFunction) {
+        return SqlHelper.retBool(getBaseMapper().update(entity, queryWrapperFunction.apply(entity)));
+    }
+
+    @Override
+    public int insertIgnore(T entity) {
+        return baseMapper.insertIgnore(entity);
+    }
+
+    @Override
+    public int replace(T entity) {
+        return baseMapper.replace(entity);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean replaceBatch(Collection<T> entityList) {
+        String sqlStatement = getSqlStatement(MySqlMethod.REPLACE_ONE);
+        return executeBatch(entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
+    }
+
+    protected String getSqlStatement(MySqlMethod sqlMethod) {
+        return getSqlStatement(mapperClass, sqlMethod);
+    }
+
+    public static String getSqlStatement(Class<?> mapper, MySqlMethod sqlMethod) {
+        return mapper.getName() + StringPool.DOT + sqlMethod.getMethod();
+    }
+
+
+}

+ 27 - 0
service/src/main/java/com/kym/service/mybatisplus/MySqlMethod.java

@@ -0,0 +1,27 @@
+package com.kym.service.mybatisplus;
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import lombok.Getter;
+
+@Getter
+public enum MySqlMethod {
+    /**
+     * 插入
+     */
+    INSERT_IGNORE_ONE("insertIgnore", "插入一条数据(选择字段插入),如果中已经存在相同的记录,则忽略当前新数据", "<script>\nINSERT IGNORE INTO %s %s VALUES %s\n</script>"),
+    /**
+     * 替换
+     */
+    REPLACE_ONE("replace", "替换一条数据(选择字段插入),存在则替换,不存在则插入", "<script>\nREPLACE INTO %s %s VALUES %s\n</script>");
+
+    private final String method;
+    private final String desc;
+    private final String sql;
+
+    MySqlMethod(String method, String desc, String sql) {
+        this.method = method;
+        this.desc = desc;
+        this.sql = sql;
+    }
+
+}

+ 33 - 0
service/src/main/java/com/kym/service/mybatisplus/MybatisPlusConfig.java

@@ -0,0 +1,33 @@
+package com.kym.service.mybatisplus;
+
+import com.baomidou.mybatisplus.core.injector.ISqlInjector;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MybatisPlusConfig {
+    /**
+     * 3.4.0 以后的配置方式
+     */
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 乐观锁
+        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+        // 分页配置
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
+        return interceptor;
+    }
+
+    /**
+     * 自定义sql注入器
+     * 关键部位重要的事情说三遍,不注入不生效,不注入不生效,不注入不生效
+     */
+    @Bean
+    public ISqlInjector iSqlInjector() {
+        return new CustomerSqlInjector();
+    }
+}

+ 61 - 0
service/src/main/java/com/kym/service/mybatisplus/injector/method/InsertIgnore.java

@@ -0,0 +1,61 @@
+package com.kym.service.mybatisplus.injector.method;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
+import com.kym.service.mybatisplus.MySqlMethod;
+import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
+import org.apache.ibatis.executor.keygen.KeyGenerator;
+import org.apache.ibatis.executor.keygen.NoKeyGenerator;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlSource;
+
+/**
+ * 扩展mybatis-plus Insert Ignore语句
+ */
+public class InsertIgnore extends AbstractMethod {
+    public InsertIgnore() {
+        this(MySqlMethod.INSERT_IGNORE_ONE.getMethod());
+    }
+
+    /**
+     * @param methodName 方法名
+     * @since 3.5.0
+     */
+    protected InsertIgnore(String methodName) {
+        super(methodName);
+    }
+
+    @Override
+    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
+        KeyGenerator keyGenerator = new NoKeyGenerator();
+        MySqlMethod sqlMethod = MySqlMethod.INSERT_IGNORE_ONE;
+        String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),
+                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
+        String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),
+                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
+        String keyProperty = null;
+        String keyColumn = null;
+        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
+        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
+            if (tableInfo.getIdType() == IdType.AUTO) {
+                /* 自增主键 */
+                keyGenerator = new Jdbc3KeyGenerator();
+                keyProperty = tableInfo.getKeyProperty();
+                keyColumn = tableInfo.getKeyColumn();
+            } else {
+                if (null != tableInfo.getKeySequence()) {
+                    keyGenerator = TableInfoHelper.genKeyGenerator(sqlMethod.getMethod(), tableInfo, builderAssistant);
+                    keyProperty = tableInfo.getKeyProperty();
+                    keyColumn = tableInfo.getKeyColumn();
+                }
+            }
+        }
+        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
+        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
+        return this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource, keyGenerator, keyProperty, keyColumn);
+    }
+}

+ 49 - 0
service/src/main/java/com/kym/service/mybatisplus/injector/method/Replace.java

@@ -0,0 +1,49 @@
+package com.kym.service.mybatisplus.injector.method;
+
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
+import com.kym.service.mybatisplus.MySqlMethod;
+import org.apache.ibatis.executor.keygen.KeyGenerator;
+import org.apache.ibatis.executor.keygen.NoKeyGenerator;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlSource;
+
+/**
+ * 扩展mybatis-plus Replace语句
+ */
+public class Replace extends AbstractMethod {
+
+    public Replace() {
+        this(MySqlMethod.REPLACE_ONE.getMethod());
+    }
+
+    protected Replace(String methodName) {
+        super(methodName);
+    }
+
+    @Override
+    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
+        KeyGenerator keyGenerator = new NoKeyGenerator();
+        MySqlMethod sqlMethod = MySqlMethod.REPLACE_ONE;
+        String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),
+                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
+        String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),
+                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
+        String keyProperty = null;
+        String keyColumn = null;
+        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
+        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
+            if (null != tableInfo.getKeySequence()) {
+                keyGenerator = TableInfoHelper.genKeyGenerator(sqlMethod.getMethod(), tableInfo, builderAssistant);
+                keyProperty = tableInfo.getKeyProperty();
+                keyColumn = tableInfo.getKeyColumn();
+            }
+        }
+        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
+        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
+        return this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource, keyGenerator, keyProperty, keyColumn);
+    }
+}