资讯动态

基于Python Flask Django微信小程序的化工危化物流车辆调度系统设计

发布时间:2026/10/14 3:52:06 来源:尧图企业网站定制
化工企业危化物流运输中的车辆调度从来都不是“谁有空谁去”这么简单。我曾经在一家做危化品运输信息化服务的企业待过一阵最大的感触是调度员每天要接十几个电话翻三四张Excel表还要盯着群里司机上传的照片确认货单任务一多漏派、错派、压车压货全跑出来了。这个“Python flask django微信小程序的化工企业危化物流运输车辆调度任务分配系统”项目核心就是要解决这类问题用一套后端管理系统加司机端小程序把任务分配、车辆调度、运输状态上报、轨迹跟踪全部串起来让调度员不用靠吼司机不用靠猜。如果你正在做类似的运输管理系统或者被化工企业的调度业务折磨过这篇文章里从技术选型到数据库设计再到调度逻辑都是我实际踩过坑之后整理出来的可以直接拿来当设计参考。1. 项目整体设计与思路拆解1.1 危化物流调度的老问题清单先把业务场景放在桌上。化工企业的危化品运输跟普通快递物流最大的区别在于货不能乱装车不能乱派人不能乱用。危险货物按照品名、UN编号、包装类别分了等级每种货对车辆、驾驶员、押运员都有资质要求。有的货需要罐式车辆有的只能用箱式车有的不能和其他品类混装有些路线经过市区还要考虑限行时间。调度员在这种环境下做任务分配至少要核对这几件事这票货属于什么危化品类别当前可选车辆里哪几辆资质和罐体类型匹配。驾驶员和押运员是否具备运输该类别危化品的从业资格证证件是否在有效期内。车辆当前是否空闲地理位置大概在哪里能不能在规定装货时间内赶到。车辆过去一周的任务强度如何不能让某辆车连续满载运行也不能让某个司机长期空等。任务有紧急程度和装卸时间窗调度时要综合考虑装货点、卸货点、预计行驶时间。光是这几项靠人脑加Excel已经很难做到实时准确。更不用说车辆在途中出现爆胎、事故、堵车、客户临时改时间调度员还要重新调整计划。所以系统设计的第一个目标就是把“人找人、人去记”变成“系统推、人来审”。1.2 这套系统的整体思路基于上面的业务分析整个系统被规划成三端协同的结构管理后台、调度服务端、司机小程序端。管理后台负责基础数据和策略配置包括车辆档案、驾驶员档案、危化品品名库、客户订单录入、运输任务创建、历史轨迹查询、报表统计。调度服务端是核心大脑负责接收新任务、匹配车辆和司机、执行调度算法、推送通知给司机端小程序。司机端小程序给驾驶员使用主要功能是查看被分配的任务、上报状态变更到达装货点、装货完成、出发、到达卸货点、卸货完成、手动或自动上传定位轨迹。三端之间通过API通信。后端的组织方式采用了“Django做业务后台Flask做调度API”的分工模式。Django这边跑管理后台、ORM模型、权限体系、常规业务接口Flask这边单独起一个轻量服务负责调度任务匹配、推送消息、轨迹接收这类高并发、逻辑相对独立的接口。小程序端用微信小程序原生框架前后端通过HTTPS接口交互定位用微信的getLocation能力消息通知走订阅消息。整体设计强调一个理念调度是高频动态的管理是低频稳定的动态和稳定不混在一个进程里排查问题的时候也会省心很多。2. 技术选型的核心逻辑Flask、Django、微信小程序各干各的活2.1 为什么是Python而不是Java或Go化工企业内部的运维团队通常不是纯互联网背景他们对Java的大型工程体系不一定熟悉Go的生态在业务管理系统上又相对年轻。Python在这类企业内部项目里接受度很高语法门槛低招人容易用Flask写一个小接口十分钟就能出来用Django搭建后台REST框架也是一天之内能出原型的事。从项目维护角度看Python全家桶做这套系统是很省力的。ORM用Django自带的那套就能覆盖绝大多数表关联写接口用Flask-RESTful能很快组织出资源路由连接数据库走SQLAlchemy封装好的连接池。再加上化工企业可能要对接地磅、GPS设备、短信平台、电子运单系统这些设备的SDK和接口协议文档里Python示例最常见。2.2 Django管后台业务Flask做轻量API很多人会问Flask和Django选一个不就行了吗实际项目中两个都可以用而且各有边界。Django自带Admin、认证系统、Migration管理、表单处理这些对于后台管理场景是很大的效率提升。比如车辆信息录入、驾驶员证件管理、危化品品名维护直接在Django Admin里配好字段和权限就能让业务人员上手操作不用额外开发一套后台页面。Flask则更适合做面向小程序和移动端的API层。调度接口有比较高的吞吐需求任务分配、状态上报这些请求可能集中在早高峰和交接班时间Flask的轻量属性让单服务部署、横向扩容都变得简单。我们把两个框架通过同一套MySQL数据库和Redis缓存连接起来Django负责写业务数据Flask负责读实时状态、执行调度预计算职责分得比较清晰。我在实际项目中见过一种做法是把Flask和Django嵌在一个进程里一个项目装多个WSGI应用。技术上能跑但部署时要小心路由冲突和依赖版本踩过几次坑之后我比较推荐分开部署哪怕在同一台服务器上用Nginx按不同的路径转发到不同端口也清爽得多。2.3 小程序窗口比App更适合司机端司机端没有选择做原生App也没有做H5而是选了微信小程序这基于几个很现实的考虑。第一化工企业的外协司机和自有司机数量不固定App需要下载安装、注册账号、维护版本很多司机手机里装了一堆App真正用起来的没几个。小程序扫码即用从微信会话里就能打开学习成本几乎为零。第二小程序提供了相对稳定的定位授权能力司机不需要理解什么经纬度坐标系点一下授权系统就能定时拿到位置。第三微信的订阅消息机制天然适合任务提醒和调度通知。调度员在后台给司机派了任务小程序通过订阅消息推送一条提醒司机打开小程序就能看到任务详情和导航路线比短信通知清晰得多。当然小程序也有局限性比如在前台长时间运行会受到系统限制定位上报在司机切换后台时会中断。我们的方案是要求司机在前端页面主动点击“开始运输”后开启定时定位上报同时在页面里提示司机保持微信前台运行并允许手动补报轨迹点。这套设计虽然不算完全自动化但胜在稳定可控不会出现服务端轨迹断档。3. 核心模块拆解从建车池到任务闭环3.1 用户角色与权限设计危化物流系统里角色不是简单的一个“用户”字段就能解决的。从业人员背景复杂有调度员、安全员、车队管理员、司机、押运员、仓库发货员。不同角色操作的范围差异很大安全员要能查看所有车辆资质和超速报警但不能修改运价司机只能看到分派给自己的任务和自己的车辆状态不能浏览其他司机的行程。我用Django自带的Permission体系做基础再扩展一个小组概念。比如“调度组”可以创建任务、调整车辆、取消运输“安全监控组”可以查看轨迹、接收违规报警、锁定车辆“车队管理组”负责车辆年检和维修记录维护。每个用户在登录时后台会返回一个权限标识列表小程序端再根据标识控制菜单和按钮显示后端接口也做二次校验防止越权调用。3.2 运输任务与危化品约束建模任务建模是整个系统的地基。化工企业的运输任务我一开始设计的时候只想到了起止地点和货物名称结果被业务人员纠正了好几次最终沉淀出以下关键字段任务单号遵循企业内部编码规则比如”YX-品类缩写-年月日-流水号“。货物信息包括品名、危规号UN编号、包装类别、危险类别、数量、重量、装载方式。运输要求包括装货开始时间窗、卸货时间窗、是否需要押运员、是否必须专车专用、温度控制要求。计价信息包括里程费、等待费、额外服务费这些一般在后台订单模块里生成调度模块只读取不修改。紧急程度用0到3的紧急级别标记级别越高调度算法里的权重越大。这里特别要关注“车辆与危化品匹配”的约束。我的做法是在车辆档案里预置一个兼容列表例如罐式车只能接液体危险品任务箱式车只能接固体包装危险品任务同时还要判断该车辆的危险货物运输资质是否覆盖当前货物的危险类别。如果不做这一步调度系统可能会派一辆仅具备三类易燃液体运输资质的车去运六类毒性物质这属于重大安全隐患系统里必须有硬性校验。3.3 调度分配的逻辑与算法细节任务分配是系统的核心算法所在。化工调度场景里任务数虽然不像快递那么海量但约束条件多所以不能用简单的先到先得。我采用的是一套基于权重的评分分配机制核心流程分两阶段先筛候选再打分排序。候选筛选用硬性约束过滤掉绝对不能派的车比如车辆状态不是空闲、资质不匹配、驾驶员资格证过期、车辆当前位置到装货点超过可接受时间、该时间窗内车辆已有其他任务。硬性过滤之后剩下的车辆进入打分环节。打分维度我设了四类任务紧迫度分数紧急级别越高的任务对候选车来说得分加成越大保证急单优先用车。距离与时间成本分数取车辆当前GPS位置到装货点的路网距离越近得分越高这个距离通过调用地图服务API获取避免使用直线距离导致实际到达时间误差太大。驾驶员工作负载均衡分数统计每位司机最近7天累计运行里程和任务单量负载越高得分越低防止司机疲劳驾驶和任务分配不均。车辆等待容忍度分数如果一个车已经空置了很久那么在下个任务里给予一定优先减少车辆闲置率。每个维度分配一个可配置的权重系数。调度页面里算法算出的前三名候选车辆会展示出来调度员可以一键选择排名第一的车辆也可以人工调整到第二或第三辆系统记录调度员最终选择的原因用来积累规则。这种“算法推荐加人工确认”的模式在化工企业里很容易被接受既提升了效率又没有把调度员完全踢出决策链条。3.4 司机小程序端的工作流小程序端的工作流按任务状态机设计。司机登录后进入首页看到的是“待接单”、“运输中”、“待送达”三个主要工作区。调度员分配任务后小程序通过微信订阅消息推送给司机。司机点击进入任务详情可以看到全部货物信息包括危险类别提醒、车辆和自身资质确认按钮、装货地址、联系人、装货时间窗。司机在装货点确认到场后点击“到场打卡”后台记录打卡时间。装货完成后点击“装货完成”同步开启轨迹上传模式小程序每30秒调用一次定位接口上传位置。到达卸货点后点击“到达”卸货完成后点击“完成交接”整个任务闭环结束。这里有几个容易被忽略的细节。第一每个状态变更都必须记录操作人和操作时间这在运输纠纷和事故追溯时非常重要。第二到货延迟要能自动触发提醒比如司机在超过时间窗30分钟仍未点击“到场打卡”小程序端和服务端都要收到预警提示。第三状态一旦切换到“运输中”司机端页面就锁定显示当前任务信息避免误触其他页面导致状态丢失。3.5 轨迹追踪与电子运单联动轨迹追踪不是一个单独的页面而是一条数据流小程序的GPS点通过接口上报到Flask服务Flask把原始坐标写入轨迹表和轨迹临时缓存同时定时任务将坐标聚合渲染到管理后台的地图上。做化工运输轨迹数据不只是给调度员看一眼车在哪更重要的是要给安全和合规部门留存凭证。我建议在设计时为每个轨迹点保留这组字段车辆ID、任务ID、司机ID、经度、纬度、速度、方向角、上报时间、数据来源自动定位还是手动补报、坐标类型。坐标类型通常用国测局坐标或者GCJ-02小程序拿到的原始坐标需要转换后才能在地图上正确显示这个问题我在第6节里会再展开。另外一个联动是电子运单。很多化工企业执行“一车一单”的管理要求纸质运单随车携带后台生成的运单PDF需要与任务关联存档。我在系统里设置了一个定时任务任务完成后自动生成运单归档表并把运输轨迹关键节点快照出场、入场、到达时间写入运单记录方便调取查看。4. 数据库结构设计与状态流转定义4.1 核心数据表划分基于业务模块数据库的核心表我分成五个组基础档案组、任务订单组、调度执行组、轨迹监控组、消息通知组。基础档案组主要是车辆表、驾驶员表、押运员表、危化品品名表、客户表。车辆表要记录车牌号、车辆类型、罐体容积或厢体尺寸、车辆状态、年检过期日、技术等级、安装的GPS设备编号、运输资质范围。驾驶员表要记录姓名、身份证号、从业资格证号、资格证类别、健康证有效期、联系方式、当前是否处于当班状态。任务订单组包括订单表、运输任务表、装卸货地址表。订单表保存业务信息运输任务表保存调度执行所必需的状态和分配结果。调度执行组包括调度记录表、车辆任务绑定表、任务状态变更日志表。调度记录表保存每一次任务分配的过程包括候选车辆列表、算法评分、最终选择车辆、人工调整原因。车辆任务绑定表记录当前有效车辆与任务的关系每辆车同一时间只能绑定一个未完成任务。轨迹监控组包括车辆轨迹表、电子围栏表、违规事件表。车辆轨迹表按月分表避免数据量过大多影响查询速度。电子围栏表用于装卸货区域的范围判断。消息通知组包括订阅消息模板表、消息发送记录表、司机阅读确认表。这里尤其要记录消息送达情况避免司机说“没收到通知”时无据可查。4.2 任务状态机运输任务从创建到结束我设计了以下状态流转状态编码状态名称说明可流转目标CREATED已创建订单生成但未进入调度待分配、已取消ALLOCATING待分配正在执行调度算法匹配候选已分配ALLOCATED已分配已选车辆和司机待司机确认运输中、分配变更、已取消TRANSPORTING运输中司机已装货出发待送达、异常挂起、已取消ARRIVED待送达到达卸货区域已完成、异常挂起COMPLETED已完成交接验收完成无SUSPENDED异常挂起发生异常终止等待处理已取消、恢复执行状态机在代码里实现时要注意一点不允许跳过状态。比如司机不能从“待分配”直接点击“运输中”必须在后台完成“分配”操作后司机端才会出现开始运输按钮。我在Django模型层里加了一个状态变化校验如果不符合状态机的合法流转直接抛出异常防止前端页面或API被恶意调用时篡改状态。5. 实操过程与核心环节实现5.1 用Django ORM搭建基础模型实际开发时我会先在Django项目里把核心model建好。以车辆模型为例字段定义如下from django.db import models class Vehicle(models.Model): plate_number models.CharField(max_length20, uniqueTrue, verbose_name车牌号) vehicle_type models.CharField(max_length20, choices[ (tank, 罐式车), (box, 厢式车), (flat, 栏板车) ], verbose_name车辆类型) tank_volume models.DecimalField(max_digits10, decimal_places2, nullTrue, blankTrue, verbose_name罐体容积) status models.CharField(max_length20, choices[ (idle, 空闲), (busy, 执行中), (maintenance, 维修), (locked, 锁定) ], defaultidle, verbose_name车辆状态) qualification_levels models.JSONField(defaultlist, verbose_name资质覆盖的危险类别列表) license_expire_date models.DateField(verbose_name道路运输证有效期) gps_device_id models.CharField(max_length50, blankTrue, verbose_nameGPS设备编号) current_location_lng models.DecimalField(max_digits9, decimal_places6, nullTrue, blankTrue) current_location_lat models.DecimalField(max_digits9, decimal_places6, nullTrue, blankTrue) class Meta: db_table vehicle这里有个很关键的写法危化品运输资质范围用JSONField存储危险类别列表。因为不同的车资质覆盖不同有的车可以运输三类和六类有的只能运输八类不用JSON存就得额外建关联表后续做兼容判断还要多查几次。用列表字段虽然整条记录看起来不够“范式”但在这个场景里效率和可配置性都更好。5.2 Flask端实现任务分配接口任务分配接口放在Flask服务里接的是调度员在后台点击“推荐车辆”的动作。接口流程分为三步拉取符合条件的空闲车辆、逐车做硬性过滤、对剩余车辆评分排序。from flask import Blueprint, request, jsonify from sqlalchemy import text dispatch_bp Blueprint(dispatch, __name__) dispatch_bp.route(/api/dispatch/recommend, methods[POST]) def recommend_vehicles(): payload request.get_json() task_id payload.get(task_id) cargo_class payload.get(cargo_class) # 1. 获取空闲车辆做硬性校验 sql text( SELECT v.id, v.plate_number, v.vehicle_type, v.qualification_levels, d.id AS driver_id, d.driver_name, d.workload_score FROM vehicle v JOIN driver d ON d.current_vehicle_id v.id AND d.status available WHERE v.status idle AND v.license_expire_date CURDATE() ) rows db.session.execute(sql).fetchall() candidates [] for row in rows: # 2. 危险类别资质兼容判断 quals row.qualification_levels or [] if cargo_class not in quals: continue # 3. 计算距离分、负载分、等待分这里略去具体地理距离查询 score self._score_vehicle(row, task_id) candidates.append({ vehicle_id: row.id, plate_number: row.plate_number, driver_name: row.driver_name, score: round(score, 2) }) candidates.sort(keylambda x: x[score], reverseTrue) return jsonify({code: 0, data: candidates[:5]})接口返回的候选车辆列表会渲染到Django后台调度页面调度员从里面选一辆确认分配。Flask服务里不落调度源数据只做推荐计算确认动作走Django接口完成避免两个服务争抢同一张表的写权限。5.3 小程序端对接与定位上报小程序端代码相对简单但有个地方我要提醒你微信的wx.getLocation接口返回的是火星坐标系如果地图服务用的是不同坐标系别直接拿去画点。我的做法是小程序拿到原始坐标后调用后端转换接口转换为WGS-84或业务约定的底图坐标系之后再存储前端再拿转换后的坐标渲染。定位上报接口设计成批量提交比如每5个轨迹点打包一次上报减少GPS在弱网环境下的请求失败率。微信小程序里关键的一段逻辑是任务状态上报function reportStatus(taskId, newStatus) { wx.request({ url: ${baseUrl}/api/task/status, method: POST, data: { task_id: taskId, status: newStatus, timestamp: Date.now(), location: currentLocation }, success(res) { if (res.data.code ! 0) { wx.showToast({ title: 状态上报失败请重试 }); } } }); }这里要特别处理好断网情况。司机在偏远的化工园区里手机信号不好是常事。如果点击“装货完成”后接口没返回页面不能卡死要弹出重试提示并把操作保存在本地缓存里等网络恢复后自动补报。我实际测试过这个机制几乎每个项目都会被用到不做离线补偿的版本上线后一定会被司机吐槽。5.4 调度评分算法落地算法在实现时我把它拆成独立的服务模块方便持续调整权重。评分函数如下def _score_vehicle(self, row, task_id): score 0 weights { urgency: 3.0, distance: 2.5, load_balance: 1.5, idle_bonus: 0.5 } urgency_score task_urgency * 10 # 紧急程度映射 distance_score max(0, 10 - distance_hours(row.current_location, task.pickup_location)) load_score max(0, 10 - (row.workload_score or 0) * 2) idle_score min(10, idle_days(row) * 5) score (weights[urgency] * urgency_score weights[distance] * distance_score weights[load_balance] * load_score weights[idle_bonus] * idle_score) return score权重系数在后台做成可配置而不是写死在代码里。因为调度策略在不同时期不一样比如年中冲量的时候可能优先缩短任务等待时间而安全检查特别严格的月份可能需要优先派发资质更全的老司机。提供一个配置页面比开发人员反复改代码发版灵活得多。6. 常见问题与排查技巧实录6.1 小程序定位漂移和坐标系混乱这是我们项目里被反馈最多的问题。部分司机反馈车辆明明停在厂区地图上显示的位置却在一公里外的公路上调度员看了心里发毛以为是危化品车被人开走了。排查后发现主要原因是定位上报频率不稳定和坐标系没统一。解决办法是把轨迹上报频率从固定间隔改为动态触发车辆静止超过3分钟后降低上报频率车辆开始移动时提高频率同时后端统一做坐标转换所有轨迹点入库前都转成标准坐标系前端再根据底图类型自适应转换。我还设置了一个轨迹异常判定规则如果同一个任务里连续两个轨迹点之间的距离超过车辆最高速度乘以时间差能到达的最大范围就把该点标记为异常点不参与路线渲染。这样即使司机手机GPS偶尔飘一下地图上也不会出现一条飞出去的轨迹。6.2 任务并发导致同一辆车被重复占用早期版本里两个调度员同时在后台打开调度页面看到同一辆空闲车同时点分配结果车辆被绑到两个任务上。数据库层面没有做约束接口层面也没加锁这问题上线第一天就暴露了。我的处理方案是在分配接口里加入行锁和状态校验dispatch_bp.route(/api/dispatch/assign, methods[POST]) def assign_vehicle(): vehicle_id request.json[vehicle_id] task_id request.json[task_id] # 使用 SELECT ... FOR UPDATE 锁住车辆记录避免并发分配 vehicle db.session.execute( text(SELECT * FROM vehicle WHERE id:vid FOR UPDATE), {vid: vehicle_id} ).fetchone() if vehicle.status ! idle: return jsonify({code: 400, msg: 车辆已被占用请重新选择}) # 更新车辆状态创建绑定记录 ...注意这里不能只依赖ORM的乐观锁版本号并发场景下可能两个请求同时读到version相同的记录最后只有一个能更新成功但另一个返回结果是“更新失败”而不是“车辆被占用”。用数据库行锁让第二个请求阻塞等待拿到锁后重新查一次状态逻辑才可靠。6.3 微信订阅消息发送超限小程序订阅消息是一次授权一次发送司机如果授权的次数用完了后续任务通知就发不出去只能再次引导司机到任务页面点击订阅按钮。我在这块做了两级兜底调度分配成功后如果订阅消息发送失败自动降级为短信通知如果短信接口也超时后台任务中心会出现一条未送达告警提醒调度员通过电话或企业内部沟通工具联系司机。单纯依赖一种通知渠道在运输行业是不可接受的多通道冗余是底线。6.4 危化品禁区与限行路段提醒化工企业配送经常要进入城市周边道路有些路段对危化品运输车辆有禁行时段。地图导航软件一般不清楚企业内部限行规则司机按普通导航开过去到了路口才发现禁行再绕路就晚了。我建议在后台维护一个自定义路区限行表按时间段记录限行区域的路段范围调度分配前做路线预规划时叠加这个限行表如果路线中涉及限行路段则自动调整方案或提高任务时长预估。这块功能看起来是个附加项但实际对调度可执行度的影响非常大。我第一次做的时候没考虑限行结果系统预估运输时间跟实际到达时间差了快两个小时后续版本加进来之后预估误差降到了二十分钟以内。7. 如果重新做一遍我会在这些地方多花功夫项目上线之后再回头看很多东西如果在设计阶段就多考虑一版可以省掉不少重构成本。下面这几条算是我个人的体会也是做化工物流调度系统时容易被低估的地方。接口要做到“状态可审计”。不仅仅是任务状态变化记录任何关键操作比如候选车辆查看、分配人工修改、任务取消原因都应该有操作日志。化工运输一旦出了安全事故倒查车辆调度记录是常事日志字段不全会被安全管理部门打回来重做。车辆和司机的资质有效期校验要前置到档案录入阶段而不是等分配任务时才校验。很多司机资格证是六年有效期看起来很长但企业自有司机几十号人总有几个临近过期的。我在后台增加了一个证照到期提醒列表提前三十天自动标记避免调度员派单时才发现某个司机资格证已经过期临时换人导致任务延误。小程序端的页面交互要尽量少让司机手动输入。危化品司机的年龄普遍偏大让他们在手机上敲文字不现实。能用按钮选择的就绝不用输入框能拍照上传的就不让填单号。我当时把“到场打卡”设计成点击大按钮加拍照验证司机反馈比之前填纸质单轻松多了操作错误率也明显下降。调度算法不要追求一步到位。很多人一开始就想做一个全自动智能调度希望系统自动把所有任务安排好调度员只负责看。但化工企业最忌惮“失控感”调度员对系统推荐的候选车如果没有确认权和控制权他是不会真正信任系统的。比较成熟的做法是先从“推荐辅助”开始让调庅员感受算法的准确率积累两三个月后再考虑半自动派单逐步减少人工干预次数。轨迹数据要在项目一开始就做好分区存储和归档策略。化工企业通常要求运输记录至少保存一段时间备查轨迹表数据量增长很快。按月分表是最省事的方案查询时按任务ID加时间范围直接路由到对应月度表归档时把超过保存周期的数据打包到冷存储避免业务库越来越大越来越慢。这套系统真正跑顺之后调度员从原来的一天打几十个电话变成只看异常告警司机从原来满世界找调度员问“下一趟去哪”变成打开小程序听指令安全员从原来月底翻一叠叠纸质单据变成直接在系统里拉报表。如果你也在做类似的企业物流调度系统可以从最痛点的一个环节比如“任务分配”或者“轨迹回传”先落地一个小闭环再慢慢扩大模块这个节奏会比一上来就铺全套更容易推进。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑