Java 实现微信小程序不同人员生成不同小程序码并追踪扫码来源

Java后台实现微信小程序不同人员生成不同小程序码并追踪扫码来源

下面我将详细介绍如何使用Java后台实现这一功能。

一、整体架构设计

  1. 前端:微信小程序
  2. 后端:Java (Spring Boot)
  3. 数据库:MySQL/其他
  4. 微信接口:调用微信小程序码生成API

二、数据库设计

1. 推广人员表(promoter)

sql 复制代码
CREATE TABLE `promoter` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL COMMENT '推广人员姓名',
  `mobile` varchar(20) COMMENT '联系电话',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2. 用户-推广关系表(user_promoter_relation)

sql 复制代码
CREATE TABLE `user_promoter_relation` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` varchar(64) NOT NULL COMMENT '小程序用户openid',
  `promoter_id` bigint NOT NULL COMMENT '推广人员ID',
  `first_scan_time` datetime NOT NULL COMMENT '首次扫码时间',
  `last_scan_time` datetime NOT NULL COMMENT '最近扫码时间',
  `scan_count` int NOT NULL DEFAULT '1' COMMENT '扫码次数',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_promoter` (`user_id`,`promoter_id`),
  KEY `idx_promoter` (`promoter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

三、Java后端实现

1. 添加微信小程序Java SDK依赖

xml 复制代码
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-miniapp</artifactId>
    <version>4.1.0</version>
</dependency>

2. 配置微信小程序参数

java 复制代码
@Configuration
public class WxMaConfiguration {
    
    @Value("${wx.miniapp.appid}")
    private String appid;
    
    @Value("${wx.miniapp.secret}")
    private String secret;
    
    @Bean
    public WxMaService wxMaService() {
        WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
        config.setAppid(appid);
        config.setSecret(secret);
        
        WxMaService service = new WxMaServiceImpl();
        service.setWxMaConfig(config);
        return service;
    }
}

3. 生成带参数的小程序码

java 复制代码
@RestController
@RequestMapping("/api/qrcode")
public class QrCodeController {
    
    @Autowired
    private WxMaService wxMaService;
    
    @Autowired
    private PromoterService promoterService;
    
    /**
     * 生成推广二维码
     * @param promoterId 推广人员ID
     * @return 二维码图片字节流
     */
    @GetMapping("/generate")
    public void generatePromoterQrCode(@RequestParam Long promoterId, 
                                     HttpServletResponse response) throws IOException {
        // 验证推广人员是否存在
        Promoter promoter = promoterService.getById(promoterId);
        if (promoter == null) {
            throw new RuntimeException("推广人员不存在");
        }
        
        // 生成小程序码
        String scene = "promoterId=" + promoterId;
        WxMaQrcodeService qrcodeService = wxMaService.getQrcodeService();
        File qrCodeFile = qrcodeService.createWxaCodeUnlimit(scene, "pages/index/index", 430, true, null, false);
        
        // 返回图片流
        response.setContentType("image/jpeg");
        try (InputStream in = new FileInputStream(qrCodeFile);
             OutputStream out = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        }
    }
}

4. 处理扫码进入事件

java 复制代码
@RestController
@RequestMapping("/api/track")
public class TrackController {
    
    @Autowired
    private UserPromoterRelationService relationService;
    
    /**
     * 记录用户扫码行为
     * @param dto 包含用户信息和推广信息
     * @return 操作结果
     */
    @PostMapping("/scan")
    public Result trackScan(@RequestBody ScanTrackDTO dto) {
        // 解析scene参数
        String scene = dto.getScene();
        Map<String, String> sceneParams = parseScene(scene);
        String promoterIdStr = sceneParams.get("promoterId");
        
        if (StringUtils.isBlank(promoterIdStr)) {
            return Result.fail("缺少推广人员参数");
        }
        
        try {
            Long promoterId = Long.parseLong(promoterIdStr);
            relationService.recordUserScan(dto.getOpenid(), promoterId);
            return Result.success();
        } catch (NumberFormatException e) {
            return Result.fail("推广人员参数格式错误");
        }
    }
    
    private Map<String, String> parseScene(String scene) {
        Map<String, String> params = new HashMap<>();
        if (StringUtils.isBlank(scene)) {
            return params;
        }
        
        String[] pairs = scene.split("&");
        for (String pair : pairs) {
            String[] kv = pair.split("=");
            if (kv.length == 2) {
                params.put(kv[0], kv[1]);
            }
        }
        return params;
    }
}

5. 用户-推广关系服务

java 复制代码
@Service
public class UserPromoterRelationServiceImpl implements UserPromoterRelationService {
    
    @Autowired
    private UserPromoterRelationMapper relationMapper;
    
    @Override
    @Transactional
    public void recordUserScan(String openid, Long promoterId) {
        // 查询是否已有记录
        UserPromoterRelation relation = relationMapper.selectByUserAndPromoter(openid, promoterId);
        
        Date now = new Date();
        if (relation == null) {
            // 新建关系记录
            relation = new UserPromoterRelation();
            relation.setUserId(openid);
            relation.setPromoterId(promoterId);
            relation.setFirstScanTime(now);
            relation.setLastScanTime(now);
            relation.setScanCount(1);
            relationMapper.insert(relation);
        } else {
            // 更新已有记录
            relation.setLastScanTime(now);
            relation.setScanCount(relation.getScanCount() + 1);
            relationMapper.updateById(relation);
        }
    }
}

四、小程序前端处理

在小程序的app.js中处理扫码进入的场景:

javascript 复制代码
App({
  onLaunch: function(options) {
    // 处理扫码进入的情况
    if (options.scene === 1047 || options.scene === 1048 || options.scene === 1049) {
      // 这些scene值表示是通过扫码进入
      const scene = decodeURIComponent(options.query.scene);
      
      // 上报扫码信息到后端
      wx.request({
        url: 'https://yourdomain.com/api/track/scan',
        method: 'POST',
        data: {
          scene: scene,
          openid: this.globalData.openid // 需要先获取用户openid
        },
        success: function(res) {
          console.log('扫码记录成功', res);
        }
      });
    }
  }
})

五、数据统计接口实现

java 复制代码
@RestController
@RequestMapping("/api/stat")
public class StatController {
    
    @Autowired
    private UserPromoterRelationMapper relationMapper;
    
    /**
     * 获取推广人员业绩统计
     * @param promoterId 推广人员ID
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return 统计结果
     */
    @GetMapping("/promoter")
    public Result getPromoterStats(@RequestParam Long promoterId,
                                 @RequestParam(required = false) @DateTimeFormat(pattern="yyyy-MM-dd") Date startDate,
                                 @RequestParam(required = false) @DateTimeFormat(pattern="yyyy-MM-dd") Date endDate) {
        
        // 构建查询条件
        QueryWrapper<UserPromoterRelation> query = new QueryWrapper<>();
        query.eq("promoter_id", promoterId);
        
        if (startDate != null) {
            query.ge("first_scan_time", startDate);
        }
        if (endDate != null) {
            query.le("first_scan_time", endDate);
        }
        
        // 执行查询
        int totalUsers = relationMapper.selectCount(query);
        List<Map<String, Object>> dailyStats = relationMapper.selectDailyStatsByPromoter(promoterId, startDate, endDate);
        
        // 返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("totalUsers", totalUsers);
        result.put("dailyStats", dailyStats);
        
        return Result.success(result);
    }
}

六、安全注意事项

  1. 参数校验:所有传入的promoterId需要验证是否存在
  2. 防刷机制:限制同一用户频繁上报扫码记录
  3. HTTPS:确保所有接口使用HTTPS协议
  4. 权限控制:推广数据统计接口需要添加权限验证
  5. 日志记录:记录所有二维码生成和扫码行为

七、扩展功能建议

  1. 二级分销:可以扩展支持多级推广关系
  2. 奖励机制:根据扫码用户的活动情况给推广人员奖励
  3. 实时通知:当有新用户扫码时,实时通知推广人员
  4. 数据分析:提供更详细的数据分析报表

通过以上Java实现,你可以完整地构建一个支持不同人员生成不同小程序码并能追踪扫码来源的系统。