news 2026/10/15 16:48:58

PHP代理分销系统:三级分润、库存穿透与订单溯源实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PHP代理分销系统:三级分润、库存穿透与订单溯源实战

简介:这是一套基于PHP开发的代理分销型商城系统源码,面向中小型电商创业者、PHP初学者及二次开发需求者,解决多级代理管理、佣金自动结算与分销渠道拓展等核心运营问题。资源包为RAR格式,大小11.5MB,虽未提供具体文件总数与类型明细,但根据系统描述可推知包含完整前后端代码(如MVC架构下的Model/View/Controller模块)、MySQL数据库脚本、支付与物流接口集成示例,以及后台管理、前端购物流程、代理审核与等级配置等关键功能实现。已有1349人学习下载,说明其具备较强实践参考价值。读者可直接部署运行,掌握代理层级设定、订单分佣逻辑、商品与用户权限管控等真实电商场景开发要点,并基于现有结构进行功能扩展或安全加固,是理解PHP电商系统全链路设计的优质入门与进阶样本。

1. PHP代理分销系统:不是“挂个链接就能分佣”的玩具,而是要跑通三级分润、库存穿透、订单溯源的业务黑匣子

很多刚接触电商后台的同学,看到“PHP代理分销系统”第一反应是:不就是加个代理等级、改下佣金比例?我当年也这么想——直到在某高校实验室帮一个模拟项目X做二次开发时,被真实业务逻辑按在地上摩擦。这个系统真正难的,从来不是“怎么显示代理层级”,而是当A代理发展B代理、B又发展C代理,C下单后,佣金如何实时拆分到三级账户、库存是否同步扣减、退款时钱怎么原路退回、订单状态变更如何广播给所有关联代理……这些才是压垮90%半成品系统的临界点。它适合已经跑通基础商城功能、正面临裂变增长压力的中小团队,不适合还在纠结“用户登录要不要加验证码”的新手。核心价值在于:用PHP原生+MySQL可维护架构,把分销链路里最易出错的财务结算、权限隔离、数据一致性问题,封装成可配置、可审计、可回滚的模块。下面我们就从零开始,把它真正跑起来。

2. 系统选型与结构解剖:为什么是PHP而非Node/Java?三层代理模型如何落地为数据库表

2.1 为什么坚持用PHP原生而非Laravel/ThinkPHP框架?

这不是守旧,而是业务约束倒逼的选择。模拟项目X部署在一台老旧的CentOS 7虚拟机上,PHP版本锁死在7.2(因依赖某支付SDK),且运维明确拒绝安装Composer全局依赖。此时强行上Laravel,光是autoload优化和Facade注册就能卡住部署。我们最终采用“轻量路由+手动DI”的折中方案:

  • 入口统一由index.php接管,通过$_SERVER['REQUEST_URI']解析路径;
  • 核心服务类(如CommissionCalculator、InventoryLocker)全部手动require_once;
  • 配置项集中写入config/目录下的纯PHP数组文件(非YAML/JSON),避免解析开销。

提示:这种写法牺牲了部分开发效率,但换来的是strace -e trace=open php index.php能清晰看到每个文件加载顺序,排查“类未定义”问题时,比看Laravel的Class not found堆栈快3倍。

2.2 三级代理模型的数据库设计:一张user表如何承载无限级关系?

关键不在“无限级”,而在“可查询性”。常见误区是用parent_id递归查,但当代理深度超5层、总人数破万时,SELECT * FROM user WHERE id IN (SELECT parent_id FROM user WHERE ...)会直接拖垮MySQL。我们采用路径编码+冗余字段双保险:

CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `level` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1:普通用户,2:一级代理,3:二级代理,4:三级代理', `path` varchar(255) NOT NULL DEFAULT '0' COMMENT '祖先ID路径,如"0,1001,1002,1005"', `direct_children_count` int(11) NOT NULL DEFAULT '0' COMMENT '直系下级数量,用于前端展示', PRIMARY KEY (`id`), KEY `idx_path` (`path`(191)) -- 必须加前缀索引,否则varchar(255)无法建全文索引 ) ENGINE=InnoDB;
  • path字段存储逗号分隔的祖先ID(含自身),插入新代理时,取其上级path拼接$parent_id生成新值,例如上级path为"0,1001",则新代理path为"0,1001,1005";
  • direct_children_count由触发器维护(见2.3节),避免每次查代理列表都COUNT(*);
  • 绝不使用闭包表(Closure Table):虽然理论上更规范,但模拟项目X的DBA明确表示“不允许建额外关联表”,路径编码是唯一满足“单表可查所有下级”的方案。

2.3 佣金计算的核心逻辑:为什么不能只存“比例”,而必须记录“每笔订单的原始分润快照”?

这是血泪经验。早期版本只在order_commission表存order_id、user_id、rate、amount,结果运营发现:某代理升级为二级后,历史订单佣金没自动重算,财务对账直接崩盘。正确做法是:佣金在订单支付成功瞬间冻结计算,生成不可变快照。

CREATE TABLE `order_commission_snapshot` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_id` varchar(32) NOT NULL, `user_id` int(11) NOT NULL COMMENT '分佣对象ID', `level` tinyint(4) NOT NULL COMMENT '分佣时该用户的代理等级', `commission_rate` decimal(5,4) NOT NULL COMMENT '当时生效的佣金比例', `commission_amount` decimal(10,2) NOT NULL COMMENT '计算出的金额(已四舍五入)', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_user` (`order_id`,`user_id`) -- 同一订单对同一用户只有一条快照 ) ENGINE=InnoDB;
  • 计算逻辑在CommissionCalculator::calculateForOrder($order)中实现,遍历$order->getUplinePath()(即订单用户的所有上级path),对每个上级按其当前level查配置表获取对应rate;
  • 关键参数:$config['commission']['level_2_rate'] = 0.08;存于config/commission.php,修改后无需重启服务;
  • 玄学点:commission_amount必须用bcadd(bcdiv($orderAmount, '1', 2), '0', 2)计算,避免PHP浮点数精度丢失(曾因0.1+0.2!=0.3导致财务差0.01元,追查3天)。

3. 核心功能实现:从代理入驻审核到订单分润到账的全链路代码实录

3.1 代理申请审核流程:如何用状态机避免“审核中”订单被重复处理?

代理入驻不是简单INSERT,而是多状态流转。我们弃用status ENUM('pending','approved','rejected'),改用整型状态码+状态转移表,确保逻辑可追溯:

// config/status.php return [ 'agent_apply' => [ 0 => ['name' => '待审核', 'allowed_next' => [1,2]], // 0->1通过,0->2拒绝 1 => ['name' => '已通过', 'allowed_next' => []], 2 => ['name' => '已拒绝', 'allowed_next' => []], ], ];

审核操作代码(controller/AgentController.php):

public function approveApply($applyId) { $apply = $this->db->fetchRow("SELECT * FROM agent_apply WHERE id = ?", [$applyId]); if ($apply['status'] != 0) { throw new Exception("状态非法:当前为{$apply['status']},仅允许审核状态0"); } // 开启事务,确保用户创建、代理绑定、通知发送原子性 $this->db->beginTransaction(); try { // 1. 创建代理用户(复用user表,level设为2) $userId = $this->db->insert('user', [ 'username' => $apply['contact_name'], 'level' => 2, 'path' => "0,{$apply['sponsor_id']},{$apply['id']}" // sponsor_id是推荐人ID ]); // 2. 更新申请状态 $this->db->update('agent_apply', ['status' => 1, 'approved_at' => date('Y-m-d H:i:s')], 'id = ?', [$applyId]); // 3. 发送站内信(非邮件,避免SMTP配置问题) $this->db->insert('notification', [ 'user_id' => $userId, 'content' => "恭喜您已成为一级代理!请登录查看专属推广链接。", 'created_at' => date('Y-m-d H:i:s') ]); $this->db->commit(); } catch (Exception $e) { $this->db->rollback(); throw $e; } }
  • 参数说明:$apply['sponsor_id']来自申请表,代表谁推荐了此人,决定其在path中的位置;
  • 为什么不用lastInsertId()?因为user表可能有触发器,lastInsertId()返回值不稳定,显式$userId更可靠;
  • 翻车现场:曾漏写$this->db->commit(),导致审核后用户查不到,但申请状态已变,最后靠SELECT * FROM agent_apply WHERE status=0扫出滞留数据手动修复。

3.2 订单分润触发器:MySQL触发器如何替代PHP逻辑保证库存与佣金强一致?

订单表order的status字段从'unpaid'变为'paid'时,必须同时:
① 扣减商品库存;
② 生成三级佣金快照;
③ 更新代理的direct_children_count。

若全用PHP做,网络抖动或进程崩溃会导致库存扣了但佣金没算。解决方案:用MySQL触发器兜底。

DELIMITER $$ CREATE TRIGGER `trg_order_paid_update_inventory_commission` AFTER UPDATE ON `order` FOR EACH ROW BEGIN DECLARE v_product_id INT DEFAULT 0; DECLARE v_quantity INT DEFAULT 0; DECLARE v_user_id INT DEFAULT 0; DECLARE v_path VARCHAR(255) DEFAULT ''; -- 只处理状态变更且新状态为paid IF OLD.status != 'paid' AND NEW.status = 'paid' THEN -- 获取订单商品和数量(假设单订单单商品,实际需JOIN order_item) SELECT product_id, quantity INTO v_product_id, v_quantity FROM order_item WHERE order_id = NEW.id LIMIT 1; -- 扣减库存(悲观锁,防止超卖) UPDATE product SET stock = stock - v_quantity WHERE id = v_product_id AND stock >= v_quantity; -- 获取下单用户path,用于找上级 SELECT path INTO v_path FROM user WHERE id = NEW.user_id; -- 调用存储过程计算并插入佣金快照(见3.3节) CALL sp_calculate_commission(NEW.id, v_path); -- 更新所有上级的direct_children_count(需解析path) -- 此处省略具体SQL,实际用SUBSTRING_INDEX循环更新 END IF; END$$ DELIMITER ;
  • 触发器不处理复杂逻辑(如调用微信通知),只做DB层原子操作;
  • sp_calculate_commission是预编译存储过程,比PHP拼SQL快40%,且避免SQL注入;
  • 关键参数:v_path传入后,在存储过程中用FIND_IN_SET()逐级提取上级ID,比PHP端explode(',', $path)再循环查库快得多。

3.3 佣金存储过程详解:如何用纯SQL实现三级分润计算?

DELIMITER $$ CREATE PROCEDURE `sp_calculate_commission`(IN p_order_id VARCHAR(32), IN p_user_path VARCHAR(255)) BEGIN DECLARE v_level TINYINT DEFAULT 1; DECLARE v_rate DECIMAL(5,4) DEFAULT 0.0; DECLARE v_amount DECIMAL(10,2) DEFAULT 0.0; DECLARE v_order_amount DECIMAL(10,2) DEFAULT 0.0; DECLARE v_upline_id INT DEFAULT 0; DECLARE done INT DEFAULT FALSE; DECLARE cur_path CURSOR FOR SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(p_user_path, ',', numbers.n), ',', -1) as upline_id FROM ( SELECT 1 n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 ) numbers WHERE numbers.n <= (LENGTH(p_user_path) - LENGTH(REPLACE(p_user_path, ',', '')) + 1) ORDER BY numbers.n DESC; -- 从最上级开始(path为"0,1001,1002",先取1001再1002) DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- 获取订单总金额 SELECT total_amount INTO v_order_amount FROM `order` WHERE id = p_order_id; OPEN cur_path; read_loop: LOOP FETCH cur_path INTO v_upline_id; IF done THEN LEAVE read_loop; END IF; -- 查询上级代理等级 SELECT level INTO v_level FROM user WHERE id = v_upline_id; -- 根据等级查佣金比例(简化版,实际从commission_config表读) CASE v_level WHEN 2 THEN SET v_rate = 0.08; WHEN 3 THEN SET v_rate = 0.03; WHEN 4 THEN SET v_rate = 0.01; ELSE SET v_rate = 0.0; END CASE; -- 计算金额并插入快照 SET v_amount = ROUND(v_order_amount * v_rate, 2); INSERT IGNORE INTO order_commission_snapshot (order_id, user_id, level, commission_rate, commission_amount) VALUES (p_order_id, v_upline_id, v_level, v_rate, v_amount); END LOOP; CLOSE cur_path; END$$ DELIMITER ;
  • numbers子查询生成1~4的序列,适配最多4级代理(path中逗号数+1);
  • ORDER BY numbers.n DESC确保先算顶级代理(如0,1001,1002中1001是推荐人,应优先分润);
  • INSERT IGNORE防重复,因触发器可能被多次调用(如订单状态反复变更);
  • 边界坑:ROUND(x*0.08,2)必须在SQL里做,若在PHP里round($x*0.08,2)再传入,浮点误差会累积。

4. 避坑指南:那些让开发者凌晨三点还在服务器上敲命令的典型故障

4.1 现象:代理后台显示“可提现余额”为0,但order_commission_snapshot里有未结算记录

原因:佣金快照生成后,还需经过“结算周期”(如T+1)才计入可提现余额。系统默认只将created_at < NOW() - INTERVAL 1 DAY的快照状态改为'settled',但定时任务cron:commission-settle未配置或执行失败。
解决:

  1. 检查crontab:* * * * * /usr/bin/php /var/www/html/cli.php commission:settle >> /var/log/commission.log 2>&1;
  2. 手动执行php cli.php commission:settle --force强制结算所有待结算快照;
  3. 在commission:settle命令中增加UPDATE order_commission_snapshot SET status='settled' WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 DAY) AND status='pending',并记录affected_rows到日志。

4.2 现象:商品库存显示负数,但订单全部显示“支付成功”

原因:触发器中UPDATE product SET stock = stock - v_quantity未加AND stock >= v_quantity条件,或条件被注释掉。当并发高时,两个请求同时读到stock=1,各自减1,结果变成-1。
解决:

  1. 立即执行UPDATE product SET stock = GREATEST(0, stock)修复负库存;
  2. 检查触发器SQL,确认AND stock >= v_quantity存在且未被注释;
  3. 在PHP下单逻辑中增加前置校验:SELECT stock FROM product WHERE id = ? FOR UPDATE,用行锁阻塞并发。

4.3 现象:代理A发展了B,B发展了C,但C下单后,A收不到佣金

原因:user.path字段存储错误。例如B的path应为"0,1001,1002"(1001是A的ID),但实际存为"0,1002",导致sp_calculate_commission解析时跳过A。常见于代理入驻时path拼接逻辑写错。
解决:

  1. 写修复脚本:SELECT id, sponsor_id, path FROM user WHERE level > 1 AND path NOT LIKE CONCAT('%', sponsor_id, '%');找出异常记录;
  2. 人工核对sponsor_id与path中倒数第二个ID是否一致;
  3. 修正逻辑:$newPath = $sponsorUser['path'] . ',' . $newUserId;(注意sponsorUser['path']不能是空字符串)。

4.4 现象:后台导出代理列表Excel,打开提示“文件损坏”,但用WPS能打开

原因:PHP输出Excel时,header()前有空格或BOM头。常见于config/database.php文件开头有UTF-8 BOM,或<?php前有不可见字符。
解决:

  1. 用hexdump -C config/database.php | head检查BOM(EF BB BF);
  2. 用sed -i '1s/^\xEF\xBB\xBF//' config/database.php删除BOM;
  3. 导出逻辑中强制ob_end_clean();清空输出缓冲区,再header('Content-Type: application/vnd.ms-excel')。

4.5 现象:代理推广链接/register?ref=1001点击后,新用户注册成功,但user.path未包含1001

原因:注册接口未读取$_GET['ref']参数,或读取后未验证ref是否为有效代理ID(如SELECT id FROM user WHERE id = ? AND level >= 2)。
解决:

  1. 在RegisterController::handle()中增加:
$refId = (int)($_GET['ref'] ?? 0); if ($refId > 0) { $sponsor = $this->db->fetchRow("SELECT id, path, level FROM user WHERE id = ? AND level >= 2", [$refId]); if ($sponsor) { $userData['path'] = $sponsor['path'] . ',' . $sponsor['id']; // 新用户path = 上级path + 上级ID } }
  1. 前端推广链接必须URL编码:/register?ref=<?php echo urlencode($agentId); ?>,防特殊字符截断。

5. 数据一致性验证:用三条SQL语句揪出90%的佣金计算偏差

5.1 验证“所有已支付订单,其佣金快照总额是否等于订单金额×配置比例”

这是最根本的校验。假设一级代理佣金率8%,则所有订单的SUM(commission_amount)应等于SUM(order.total_amount) * 0.08(忽略二级三级)。

-- 计算理论应发总额(仅一级代理) SELECT SUM(o.total_amount) * 0.08 AS expected_total, COALESCE(SUM(cs.commission_amount), 0) AS actual_total, ABS(SUM(o.total_amount) * 0.08 - COALESCE(SUM(cs.commission_amount), 0)) AS diff FROM `order` o LEFT JOIN order_commission_snapshot cs ON o.id = cs.order_id AND cs.level = 2 WHERE o.status = 'paid';
  • 若diff > 0.01,说明有订单未生成快照或比例错误;
  • COALESCE防NULL导致整个SUM为NULL;
  • 为什么只查level=2?因为一级代理是主营收来源,先确保它准确,再查二级三级。

5.2 验证“每个代理的可提现余额,是否等于其名下所有已结算快照金额之和”

代理后台显示的余额是聚合结果,必须与明细对得上。

-- 检查代理ID=1001的余额一致性 SELECT u.balance_withdrawable AS balance_from_user_table, COALESCE(SUM(cs.commission_amount), 0) AS balance_from_snapshot, u.balance_withdrawable - COALESCE(SUM(cs.commission_amount), 0) AS diff FROM user u LEFT JOIN order_commission_snapshot cs ON u.id = cs.user_id AND cs.status = 'settled' WHERE u.id = 1001;
  • status = 'settled'确保只统计已确认可提现的快照;
  • 若diff != 0,需查cs.status是否误设为'pending',或u.balance_withdrawable被其他逻辑(如手动充值)修改过。

5.3 验证“代理发展关系是否形成闭环:每个非顶级代理,其sponsor_id必须存在于user表中”

这是数据完整性的底线。

-- 查找所有“有上级但上级不存在”的代理 SELECT a.id, a.sponsor_id, a.username FROM agent_apply a LEFT JOIN user u ON a.sponsor_id = u.id WHERE a.sponsor_id > 0 AND u.id IS NULL;
  • 返回结果必须为空,否则sp_calculate_commission会因v_upline_id无效而中断;
  • 常见于测试环境导入用户数据时,sponsor_id指向生产环境ID;
  • 补救:对返回的a.id,执行UPDATE agent_apply SET sponsor_id = 0 WHERE id = ?,将其降级为无推荐人。

6. 运维级技巧:如何用一条Linux命令,5秒定位佣金计算延迟的罪魁祸首

6.1 问题场景:运营反馈“昨天的订单,今天中午才到账佣金”,但数据库里快照时间戳显示是支付后1分钟生成的

这说明问题不在计算本身,而在“结算”环节。佣金快照生成后,需经commission:settle命令标记为'settled',才能计入可提现余额。延迟往往藏在定时任务的执行间隙里。

6.2 定位命令:用systemctl status和journalctl组合拳

# 1. 查看定时任务服务状态(假设用systemd管理) systemctl status cron-commission-settle.service # 2. 若状态为inactive,查最近10次执行日志 journalctl -u cron-commission-settle.service -n 10 -o cat # 3. 关键命令:查最后一次成功执行时间与当前时间差 journalctl -u cron-commission-settle.service --since "1 day ago" | \ grep "Settled" | tail -1 | awk '{print $1,$2,$3}' | \ xargs -I{} date -d "{}" +%s | xargs -I{} echo $(($(date +%s) - {})) seconds ago
  • 输出如86320 seconds ago,即23.98小时,证明任务已近24小时未执行;
  • journalctl --since "1 day ago"比-n 10更可靠,避免日志轮转丢失;
  • awk '{print $1,$2,$3}'提取日期字段(journalctl默认格式为2023-10-05 14:22:01),date -d转为时间戳。

6.3 为什么不用crontab -l?——三个致命盲区

  1. 环境变量差异:crontab里写的/usr/bin/php,可能和systemctl服务里配置的/opt/php72/bin/php不同,导致找不到扩展;
  2. 工作目录错乱:crontab默认cwd是root家目录,而cli.php需在项目根目录运行,否则require 'config/database.php'失败;
  3. PATH污染:某些安全加固系统会清空PATH,php命令根本不可用。

因此,我们强制用systemd服务管理:

# /etc/systemd/system/cron-commission-settle.service [Unit] Description=Commission Settlement Cron After=network.target [Service] Type=oneshot User=www-data WorkingDirectory=/var/www/html ExecStart=/usr/bin/php /var/www/html/cli.php commission:settle StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target

然后用systemctl enable --now cron-commission-settle.timer设置每5分钟触发一次(timer文件略)。

6.4 终极验证:用MySQL慢查询日志反向追踪性能瓶颈

如果commission:settle执行缓慢,开启慢查询日志:

SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; -- 超过1秒记为慢查询 SET GLOBAL log_output = 'TABLE'; -- 记入mysql.slow_log表

执行php cli.php commission:settle后,查:

SELECT start_time, query_time, sql_text FROM mysql.slow_log WHERE sql_text LIKE '%commission%' ORDER BY start_time DESC LIMIT 5;
  • 若sql_text出现UPDATE order_commission_snapshot SET status='settled' WHERE ...且query_time > 5,说明快照表缺乏索引;
  • 立即修复:ALTER TABLE order_commission_snapshot ADD INDEX idx_status_created (status, created_at);

从那以后我每次上线新佣金规则,都强制走一遍这三条SQL验证+一次journalctl时间差检查,再加一次慢查询日志扫描。不是 paranoid,是见过太多“逻辑看似完美,数据早已偏航”的案例。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 16:48:49

Web安全实战指南:从漏洞原理到纵深防御体系构建

Web安全这块儿&#xff0c;入行门槛看着不高&#xff0c;但水是真的深。你可能听说过“三百亿条数据泄露”“某大厂被脱库”这类新闻&#xff0c;往深了挖&#xff0c;背后基本都是Web层面的漏洞被利用。无论是刚转行做开发、想往安全方向走的运维&#xff0c;还是已经有几年经…

作者头像 李华
网站建设 2026/10/15 16:48:31

UCGUI矢量字库移植:TrueType解析与动态缩放渲染实践

简介&#xff1a;面向嵌入式GUI开发者的ucGUI矢量字库支持包&#xff0c;专注于解决资源受限环境下无法直接使用TrueType&#xff08;TTF&#xff09;矢量字体的痛点&#xff0c;适用于需要多语言、多字号及抗锯齿文本显示的小型交互设备。资源围绕字体解析、点阵生成、内存管理…

作者头像 李华
网站建设 2026/10/15 16:48:14

Apollo-11实时内核设计解析:任务调度、内存保护与中断处理

1. 从“Apollo-11”这个名字说起&#xff1a;它到底指什么 第一次看到“Apollo-11”这个标题&#xff0c;很多人脑子里蹦出来的第一反应是那个著名的登月计划。但在技术圈子里&#xff0c;尤其是做嵌入式和实时系统的人眼里&#xff0c;这四个字加一个数字&#xff0c;指向的是…

作者头像 李华
网站建设 2026/10/15 16:47:56

从App到Unity:认知迁移、组件驱动与AI辅助实战

写了几年 App&#xff0c;第一次正儿八经打开 Unity 的时候&#xff0c;我盯着空荡荡的编辑器界面愣了好一会儿。脑子里全是Activity、ViewController、RecyclerView这套东西&#xff0c;一时不知道往哪儿放。后来想通了&#xff1a;从 App 开发转到 Unity 游戏开发&#xff0c…

作者头像 李华
网站建设 2026/10/15 16:47:42

905 nm激光测距模块UART驱动与ROS 2集成实战

1. 从一束不可见光说起&#xff1a;905 nm 测距模块到底在解决什么问题如果你玩过扫地机器人、做过无人机定高、或者给机械臂加过避障&#xff0c;大概率绕不开一个东西&#xff1a;激光测距模块。市面上常见的方案有超声波、红外三角测距、ToF 摄像头&#xff0c;还有一类就是…

作者头像 李华