Java 实现微信小程序不同人员生成不同小程序码并追踪扫码来源(最新推荐)

 更新时间:2025年06月05日 08:58:02   作者:VipSoft  
这篇文章主要介绍了Java实现微信小程序不同人员生成不同小程序码并追踪扫码来源,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

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

一、整体架构设计

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

二、数据库设计

1. 推广人员表(promoter)

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)

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依赖

<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-miniapp</artifactId>
    <version>4.1.0</version>
</dependency>

2. 配置微信小程序参数

@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. 生成带参数的小程序码

@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. 处理扫码进入事件

@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. 用户-推广关系服务

@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中处理扫码进入的场景:

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);
        }
      });
    }
  }
})

五、数据统计接口实现

@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);
    }
}

六、安全注意事项

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

七、扩展功能建议

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

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

到此这篇关于Java 实现微信小程序不同人员生成不同小程序码并追踪扫码来源的文章就介绍到这了,更多相关Java 实现微信小程序不同人员生成不同小程序码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java根据当前时间获取yyyy-MM-dd HH:mm:ss标准格式的时间代码示例

    java根据当前时间获取yyyy-MM-dd HH:mm:ss标准格式的时间代码示例

    在Java中可以使用java.time包中的LocalDateTime类和DateTimeFormatter类来获取并格式化当前时间为yyyy-MM-dd HH:mm:ss的格式,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-10-10
  • Java算法设计与分析分治算法

    Java算法设计与分析分治算法

    这篇文章主要介绍了Java算法设计与分析分治算法,一般分治算法在正文中分解为两个即以上的递归调用,并且子类问题一般是不想交的
    2022-07-07
  • Java获取堆栈信息的三种方法小结

    Java获取堆栈信息的三种方法小结

    在Java编程中,获取堆栈信息对于调试和故障排除非常重要,Java提供了多种方式来获取当前线程的堆栈信息,下面就跟随小编一起学习一下常用的三种吧
    2024-03-03
  • OpenFeign实现自定义Http请求头

    OpenFeign实现自定义Http请求头

    这篇文章主要介绍了OpenFeign实现自定义Http请求头方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2026-03-03
  • Java并发包工具类CountDownLatch的应用详解

    Java并发包工具类CountDownLatch的应用详解

    CountDownLatch是Java并发包中非常实用的一个工具类,它可以帮助我们实现线程之间的同步和协作。本文主要介绍了CountDownLatch的应用场景及最佳实践,希望对大家有所帮助
    2023-04-04
  • Java常用的时间工具类实例

    Java常用的时间工具类实例

    这篇文章主要介绍了Java常用的时间工具类,结合具体实例形式分析了java日期时间的常用转换、判断、输出相关操作技巧,需要的朋友可以参考下
    2017-06-06
  • JAVA读取文本文件内容实例代码

    JAVA读取文本文件内容实例代码

    这篇文章主要给大家介绍了关于JAVA读取文本文件内容的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • Mybatis实现自定义的typehandler三步曲

    Mybatis实现自定义的typehandler三步曲

    这篇文章主要介绍了Mybatis实现自定义的typehandler三步曲的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-07-07
  • 一篇文章带你入门java方法

    一篇文章带你入门java方法

    这篇文章主要介绍了java基础之方法详解,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-08-08
  • RocketMq 消息重试机制及死信队列详解

    RocketMq 消息重试机制及死信队列详解

    这篇文章主要为大家介绍了RocketMq 消息重试机制及死信队列详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10

最新评论