Procházet zdrojové kódy

公众号回调修改

yusm před 4 týdny
rodič
revize
b7e53b60af
16 změnil soubory, kde provedl 768 přidání a 0 odebrání
  1. 201 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatCallbackController.java
  2. 21 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatQrcodeScanController.java
  3. 21 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatUserFollowController.java
  4. 128 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/WechatQrcodeScan.java
  5. 84 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/WechatUserFollow.java
  6. 31 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/WechatQrcodeScanMapper.java
  7. 39 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/WechatUserFollowMapper.java
  8. 16 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/WechatQrcodeScanService.java
  9. 16 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/WechatUserFollowService.java
  10. 54 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesmanStatsService.java
  11. 64 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatApiService.java
  12. 20 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatQrcodeScanServiceImpl.java
  13. 20 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatUserFollowServiceImpl.java
  14. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/application.yml
  15. 29 0
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/WechatQrcodeScanMapper.xml
  16. 22 0
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/WechatUserFollowMapper.xml

+ 201 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatCallbackController.java

@@ -0,0 +1,201 @@
+package com.management.platform.controller;
+
+import com.management.platform.entity.WechatQrcodeScan;
+import com.management.platform.entity.WechatUserFollow;
+import com.management.platform.mapper.WechatQrcodeScanMapper;
+import com.management.platform.mapper.WechatUserFollowMapper;
+import com.management.platform.service.impl.WechatApiService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/wechat/callback")
+@Slf4j
+public class WechatCallbackController {
+    private final String TOKEN = "FireRockCRM2025";
+    private final WechatQrcodeScanMapper scanMapper;
+    private final WechatUserFollowMapper followMapper;
+    private final WechatApiService wechatApiService;
+    
+    public WechatCallbackController(WechatQrcodeScanMapper scanMapper, 
+                                  WechatUserFollowMapper followMapper,
+                                  WechatApiService wechatApiService) {
+        this.scanMapper = scanMapper;
+        this.followMapper = followMapper;
+        this.wechatApiService = wechatApiService;
+    }
+
+    // 微信配置验证接口(GET请求)
+    @GetMapping
+    public String validate(@RequestParam("signature") String signature,
+                           @RequestParam("timestamp") String timestamp,
+                           @RequestParam("nonce") String nonce,
+                           @RequestParam("echostr") String echostr) {
+
+        // 1. 将Token、timestamp、nonce按字典序排序
+        String[] arr = new String[]{TOKEN, timestamp, nonce};
+        Arrays.sort(arr);
+
+        // 2. 拼接后SHA1加密
+        String joined = String.join("", arr);
+        String calculatedSignature = DigestUtils.sha1Hex(joined);
+
+        // 3. 验证签名
+        if (calculatedSignature.equals(signature)) {
+            return echostr; // 验证成功返回echostr
+        }
+        log.info("Invalid signature==>GET请求验证失败");
+        return "Invalid signature"; // 验证失败
+    }
+    
+    @PostMapping(produces = "application/xml;charset=UTF-8")
+    public String handleCallback(@RequestBody String xmlData, HttpServletRequest request) {
+        try {
+            Document document = DocumentHelper.parseText(xmlData);
+            Element root = document.getRootElement();
+            
+            String msgType = root.elementText("MsgType");
+            if (!"event".equals(msgType)) {
+                return successResponse(root);
+            }
+            
+            String event = root.elementText("Event");
+            String openId = root.elementText("FromUserName");
+            String eventKey = root.elementText("EventKey");
+            String ticket = root.elementText("Ticket");
+            String createTime = root.elementText("CreateTime");
+            
+            // 处理关注/扫码事件
+            if ("subscribe".equals(event) || "SCAN".equals(event)) {
+                handleSubscribeOrScan(event, openId, eventKey, ticket, createTime, request);
+            } 
+            // 处理取消关注事件
+            else if ("unsubscribe".equals(event)) {
+                handleUnsubscribe(openId);
+            }
+
+            log.info("回调成功");
+            log.info("回调返回==>"+successResponse(root));
+            return successResponse(root);
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.info("回调失败==>"+e.getMessage());
+            return "";
+        }
+    }
+    
+    private void handleSubscribeOrScan(String event, String openId, String eventKey, 
+                                     String ticket, String createTime, 
+                                     HttpServletRequest request) {
+        // 提取销售人员ID
+        String salesmanId = eventKey.startsWith("qrscene_") ? 
+            eventKey.substring(8) : eventKey;
+        
+        // 获取用户IP
+        String ipAddress = getClientIp(request);
+        
+        // 获取用户信息
+        Map<String, Object> userInfo = wechatApiService.getUserInfo(openId);
+        
+        // 保存扫码记录
+        WechatQrcodeScan scanRecord = new WechatQrcodeScan();
+        scanRecord.setOpenId(openId);
+        scanRecord.setSalesmanId(salesmanId);
+        scanRecord.setEventType(event);
+        scanRecord.setTicket(ticket);
+        scanRecord.setScanTime(fromUnixTime(Long.parseLong(createTime)));
+        scanRecord.setIpAddress(ipAddress);
+        scanRecord.setIsNewFollower("subscribe".equals(event));
+        
+        if (userInfo != null) {
+            scanRecord.setNickname((String) userInfo.get("nickname"));
+            scanRecord.setGender((Integer) userInfo.get("sex"));
+            scanRecord.setCity((String) userInfo.get("city"));
+            scanRecord.setProvince((String) userInfo.get("province"));
+            scanRecord.setCountry((String) userInfo.get("country"));
+            scanRecord.setAvatarUrl((String) userInfo.get("headimgurl"));
+        }
+        
+        scanMapper.insert(scanRecord);
+        log.info("保存扫码记录成功");
+        
+        // 如果是新关注,更新用户关注表
+        if ("subscribe".equals(event)) {
+            WechatUserFollow followRecord = followMapper.findByOpenId(openId);
+            if (followRecord == null) {
+                followRecord = new WechatUserFollow();
+                followRecord.setOpenId(openId);
+                followRecord.setIsFollow(true);
+                followRecord.setFollowTime(LocalDateTime.now());
+                followRecord.setSalesmanId(salesmanId);
+                followMapper.insert(followRecord);
+                log.info("用户新关注成功");
+            } else {
+                followRecord.setIsFollow(true);
+                followRecord.setFollowTime(LocalDateTime.now());
+                followRecord.setSalesmanId(salesmanId);
+                followMapper.update(followRecord);
+                log.info("更新用户关注成功");
+            }
+        }
+    }
+    
+    private void handleUnsubscribe(String openId) {
+        WechatUserFollow followRecord = followMapper.findByOpenId(openId);
+        if (followRecord != null) {
+            followRecord.setIsFollow(false);
+            followRecord.setUnfollowTime(LocalDateTime.now());
+            followMapper.update(followRecord);
+            log.info("用户取消关注成功");
+        }
+    }
+    
+    private String successResponse(Element root) {
+        String fromUser = root.elementText("FromUserName");
+        String toUser = root.elementText("ToUserName");
+        
+        return String.format(
+            "<xml>" +
+            "<ToUserName><![CDATA[%s]]></ToUserName>" +
+            "<FromUserName><![CDATA[%s]]></FromUserName>" +
+            "<CreateTime>%d</CreateTime>" +
+            "<MsgType><![CDATA[text]]></MsgType>" +
+            "<Content><![CDATA[success]]></Content>" +
+            "</xml>",
+            fromUser, toUser, System.currentTimeMillis() / 1000
+        );
+    }
+    
+    private String getClientIp(HttpServletRequest request) {
+        String ip = request.getHeader("X-Forwarded-For");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return ip;
+    }
+
+    // Unix时间戳(秒) -> LocalDateTime
+    public static LocalDateTime fromUnixTime(long unixTime) {
+        return LocalDateTime.ofInstant(
+                Instant.ofEpochSecond(unixTime),
+                ZoneId.systemDefault()
+        );
+    }
+}

+ 21 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatQrcodeScanController.java

@@ -0,0 +1,21 @@
+package com.management.platform.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 微信二维码扫码记录表 前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+@RestController
+@RequestMapping("/wechat-qrcode-scan")
+public class WechatQrcodeScanController {
+
+}
+

+ 21 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatUserFollowController.java

@@ -0,0 +1,21 @@
+package com.management.platform.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 微信用户关注状态表 前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+@RestController
+@RequestMapping("/wechat-user-follow")
+public class WechatUserFollowController {
+
+}
+

+ 128 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/WechatQrcodeScan.java

@@ -0,0 +1,128 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 微信二维码扫码记录表
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class WechatQrcodeScan extends Model<WechatQrcodeScan> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户OpenID
+     */
+    @TableField("open_id")
+    private String openId;
+
+    /**
+     * 销售人员ID(场景值)
+     */
+    @TableField("salesman_id")
+    private String salesmanId;
+
+    /**
+     * 事件类型(subscribe/SCAN/unsubscribe)
+     */
+    @TableField("event_type")
+    private String eventType;
+
+    /**
+     * 二维码ticket
+     */
+    @TableField("ticket")
+    private String ticket;
+
+    /**
+     * 扫码时间
+     */
+    @TableField("scan_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime scanTime;
+
+    /**
+     * 用户IP地址
+     */
+    @TableField("ip_address")
+    private String ipAddress;
+
+    /**
+     * 是否新关注用户
+     */
+    @TableField("is_new_follower")
+    private Boolean isNewFollower;
+
+    /**
+     * 用户昵称
+     */
+    @TableField("nickname")
+    private String nickname;
+
+    /**
+     * 性别:0未知,1男,2女
+     */
+    @TableField("gender")
+    private Integer gender;
+
+    /**
+     * 城市
+     */
+    @TableField("city")
+    private String city;
+
+    /**
+     * 省份
+     */
+    @TableField("province")
+    private String province;
+
+    /**
+     * 国家
+     */
+    @TableField("country")
+    private String country;
+
+    /**
+     * 头像URL
+     */
+    @TableField("avatar_url")
+    private String avatarUrl;
+
+    /**
+     * 创建时间
+     */
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 84 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/WechatUserFollow.java

@@ -0,0 +1,84 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 微信用户关注状态表
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class WechatUserFollow extends Model<WechatUserFollow> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户OpenID
+     */
+    @TableField("open_id")
+    private String openId;
+
+    /**
+     * 是否关注:0否,1是
+     */
+    @TableField("is_follow")
+    private Boolean isFollow;
+
+    /**
+     * 关注时间
+     */
+    @TableField("follow_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime followTime;
+
+    /**
+     * 取消关注时间
+     */
+    @TableField("unfollow_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime unfollowTime;
+
+    /**
+     * 首次关注的销售人员ID
+     */
+    @TableField("salesman_id")
+    private String salesmanId;
+
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 31 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/WechatQrcodeScanMapper.java

@@ -0,0 +1,31 @@
+package com.management.platform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.WechatQrcodeScan;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Options;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * <p>
+ * 微信二维码扫码记录表 Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+public interface WechatQrcodeScanMapper extends BaseMapper<WechatQrcodeScan> {
+    @Insert("INSERT INTO wechat_qrcode_scan(open_id, salesman_id, event_type, ticket, scan_time, ip_address, " +
+            "is_new_follower, nickname, gender, city, province, country, avatar_url, create_time) " +
+            "VALUES(#{openId}, #{salesmanId}, #{eventType}, #{ticket}, #{scanTime}, #{ipAddress}, " +
+            "#{isNewFollower}, #{nickname}, #{gender}, #{city}, #{province}, #{country}, #{avatarUrl}, NOW())")
+    @Options(useGeneratedKeys = true, keyProperty = "id")
+    int insert(WechatQrcodeScan record);
+
+    @Select("SELECT COUNT(*) FROM wechat_qrcode_scan WHERE salesman_id = #{salesmanId} AND is_new_follower = 1")
+    int countNewFollowersBySalesmanId(String salesmanId);
+
+    @Select("SELECT COUNT(*) FROM wechat_qrcode_scan WHERE salesman_id = #{salesmanId}")
+    int countBySalesmanId(String salesmanId);
+
+}

+ 39 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/WechatUserFollowMapper.java

@@ -0,0 +1,39 @@
+package com.management.platform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.WechatUserFollow;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Options;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ * <p>
+ * 微信用户关注状态表 Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+public interface WechatUserFollowMapper extends BaseMapper<WechatUserFollow> {
+    @Select("SELECT * FROM wechat_user_follow WHERE open_id = #{openId}")
+    WechatUserFollow findByOpenId(String openId);
+
+    @Insert("INSERT INTO wechat_user_follow(open_id, is_follow, follow_time, salesman_id) " +
+            "VALUES(#{openId}, #{isFollow}, #{followTime}, #{salesmanId})")
+    @Options(useGeneratedKeys = true, keyProperty = "id")
+    int insert(WechatUserFollow record);
+
+    @Update("UPDATE wechat_user_follow SET is_follow = #{isFollow}, " +
+            "follow_time = #{followTime}, unfollow_time = #{unfollowTime}, " +
+            "salesman_id = #{salesmanId} WHERE open_id = #{openId}")
+    int update(WechatUserFollow record);
+
+    @Select("SELECT COUNT(*) FROM wechat_user_follow " +
+            "WHERE salesman_id = #{salesmanId} AND is_follow = 1")
+    int countActiveFollowersBySalesmanId(String salesmanId);
+
+    @Select("SELECT COUNT(*) FROM wechat_user_follow " +
+            "WHERE salesman_id = #{salesmanId} AND is_follow = 0")
+    int countUnfollowersBySalesmanId(String salesmanId);
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/WechatQrcodeScanService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.WechatQrcodeScan;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 微信二维码扫码记录表 服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+public interface WechatQrcodeScanService extends IService<WechatQrcodeScan> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/WechatUserFollowService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.WechatUserFollow;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 微信用户关注状态表 服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+public interface WechatUserFollowService extends IService<WechatUserFollow> {
+
+}

+ 54 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesmanStatsService.java

@@ -0,0 +1,54 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.mapper.WechatQrcodeScanMapper;
+import com.management.platform.mapper.WechatUserFollowMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class SalesmanStatsService {
+
+    private final WechatQrcodeScanMapper scanMapper;
+    private final WechatUserFollowMapper followMapper;
+
+    public SalesmanStatsService(WechatQrcodeScanMapper scanMapper,
+                                WechatUserFollowMapper followMapper) {
+        this.scanMapper = scanMapper;
+        this.followMapper = followMapper;
+    }
+
+    /**
+     * 获取销售人员客户统计详情
+     */
+    public Map<String, Object> getSalesmanStatsDetail(String salesmanId) {
+        Map<String, Object> stats = new HashMap<>();
+
+        // 1. 扫码总次数
+        int totalScans = scanMapper.countBySalesmanId(salesmanId);
+
+        // 2. 新关注客户数(通过扫码关注)
+        int newFollowers = scanMapper.countNewFollowersBySalesmanId(salesmanId);
+
+        // 3. 当前有效客户数(关注中)
+        int activeFollowers = followMapper.countActiveFollowersBySalesmanId(salesmanId);
+
+        // 4. 已流失客户数(取消关注)
+        int unfollowers = followMapper.countUnfollowersBySalesmanId(salesmanId);
+
+        // 5. 扫码但未关注数
+        int scanButNotFollow = totalScans - newFollowers;
+
+        stats.put("salesmanId", salesmanId);
+        stats.put("totalScans", totalScans);
+        stats.put("newFollowers", newFollowers);
+        stats.put("activeFollowers", activeFollowers);
+        stats.put("unfollowers", unfollowers);
+        stats.put("scanButNotFollow", scanButNotFollow);
+        stats.put("conversionRate", totalScans > 0 ?
+                String.format("%.2f%%", newFollowers * 100.0 / totalScans) : "0%");
+
+        return stats;
+    }
+}

+ 64 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatApiService.java

@@ -0,0 +1,64 @@
+package com.management.platform.service.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class WechatApiService {
+    
+    @Value("${weixin.APP_ID}")
+    private String appId;
+    
+    @Value("${weixin.APP_SECRET}")
+    private String appSecret;
+    
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    
+    public Map<String, Object> getUserInfo(String openId) {
+        String accessToken = getAccessToken();
+        if (accessToken == null) {
+            return null;
+        }
+        
+        String url = String.format(
+            "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN",
+            accessToken, openId);
+        
+        try (CloseableHttpClient client = HttpClients.createDefault()) {
+            HttpGet request = new HttpGet(url);
+            String response = EntityUtils.toString(client.execute(request).getEntity());
+            log.info("获取用户信息===>"+response);
+            return objectMapper.readValue(response, Map.class);
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.info("获取用户信息失败==>"+e.getMessage());
+            return null;
+        }
+    }
+    
+    private String getAccessToken() {
+        String url = String.format(
+            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
+            appId, appSecret);
+        
+        try (CloseableHttpClient client = HttpClients.createDefault()) {
+            HttpGet request = new HttpGet(url);
+            String response = EntityUtils.toString(client.execute(request).getEntity());
+            Map<String, Object> result = objectMapper.readValue(response, Map.class);
+            log.info("获取企业的token==>"+result);
+            return (String) result.get("access_token");
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.info("获取企业对应的token失败==>"+e.getMessage());
+            return null;
+        }
+    }
+}

+ 20 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatQrcodeScanServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.WechatQrcodeScan;
+import com.management.platform.mapper.WechatQrcodeScanMapper;
+import com.management.platform.service.WechatQrcodeScanService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 微信二维码扫码记录表 服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+@Service
+public class WechatQrcodeScanServiceImpl extends ServiceImpl<WechatQrcodeScanMapper, WechatQrcodeScan> implements WechatQrcodeScanService {
+
+}

+ 20 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatUserFollowServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.WechatUserFollow;
+import com.management.platform.mapper.WechatUserFollowMapper;
+import com.management.platform.service.WechatUserFollowService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 微信用户关注状态表 服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-21
+ */
+@Service
+public class WechatUserFollowServiceImpl extends ServiceImpl<WechatUserFollowMapper, WechatUserFollow> implements WechatUserFollowService {
+
+}

+ 2 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/resources/application.yml

@@ -181,3 +181,5 @@ weixin:
      QR_STR_SCENE: QR_STR_SCENE
      QR_LIMIT_SCENE: QR_LIMIT_SCENE
      QR_LIMIT_STR_SCENE: QR_LIMIT_STR_SCENE
+  APP_ID: wx1c1d8fc81bc073a8
+  APP_SECRET: 17ad07f90ee845f99f4c1605647ef755

+ 29 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/WechatQrcodeScanMapper.xml

@@ -0,0 +1,29 @@
+<?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.management.platform.mapper.WechatQrcodeScanMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.WechatQrcodeScan">
+        <id column="id" property="id" />
+        <result column="open_id" property="openId" />
+        <result column="salesman_id" property="salesmanId" />
+        <result column="event_type" property="eventType" />
+        <result column="ticket" property="ticket" />
+        <result column="scan_time" property="scanTime" />
+        <result column="ip_address" property="ipAddress" />
+        <result column="is_new_follower" property="isNewFollower" />
+        <result column="nickname" property="nickname" />
+        <result column="gender" property="gender" />
+        <result column="city" property="city" />
+        <result column="province" property="province" />
+        <result column="country" property="country" />
+        <result column="avatar_url" property="avatarUrl" />
+        <result column="create_time" property="createTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, open_id, salesman_id, event_type, ticket, scan_time, ip_address, is_new_follower, nickname, gender, city, province, country, avatar_url, create_time
+    </sql>
+
+</mapper>

+ 22 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/WechatUserFollowMapper.xml

@@ -0,0 +1,22 @@
+<?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.management.platform.mapper.WechatUserFollowMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.WechatUserFollow">
+        <id column="id" property="id" />
+        <result column="open_id" property="openId" />
+        <result column="is_follow" property="isFollow" />
+        <result column="follow_time" property="followTime" />
+        <result column="unfollow_time" property="unfollowTime" />
+        <result column="salesman_id" property="salesmanId" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, open_id, is_follow, follow_time, unfollow_time, salesman_id, create_time, update_time
+    </sql>
+
+</mapper>