SpringBoot+Vue电动车租赁系统实战:订单状态机与计费逻辑设计
最近完整落地了一个基于 SpringBoot Vue 的电动车租赁服务系统从需求梳理、数据库设计到前后端编码、部署上线全部走了一遍。趁着对这个项目的记忆还热乎把整个设计和实现过程整理出来重点放在业务建模、订单状态流转、计费逻辑、地图接入这些“看着简单、做着容易翻车”的环节给准备做同类项目的人一份能直接参考的实操记录。这个系统解决的痛点很直接传统租车要么走线下登记要么用 Excel 人工管理车辆停在哪个位置、电量还剩多少、谁正在骑、骑了多久、该扣多少钱全靠人肉核对一旦车辆多了管理成本直线上升。电动车租赁系统把“找车—扫码开锁—骑行—计费—还车—结算”整条链路搬到了线上用户在 H5 / Web 端完成操作运营人员在后台统一管理车辆、订单和计费规则。整个项目从业务逻辑上讲就是一个典型的“低并发但高状态复杂度”的系统非常适合用来理解前后端分离项目的完整落地过程。如果你想做 SpringBoot 方向的毕业设计或者已经会写 CRUD、想进阶到真正能处理业务状态流转和并发问题的开发再或者公司内部需要搭一套“小程序/App 后台 硬件设备”的租赁类系统这篇文章里的设计方案、核心代码思路、踩坑记录都有可以直接搬走的部分。1. 项目整体方案设计拆解1.1 电动车租赁业务的核心痛点做这类型系统第一步不是建表而是先想清楚“业务到底在管什么”。表面上是管车本质上是管三样东西人、车、钱。人是用户和运营人员车是分布在各个站点的电动车辆钱是押金、骑行时长费、优惠券和退款。租赁系统的复杂度和价值全部体现在这三者之间的状态流转上。一辆车在系统里有哪些状态空闲、已被预约、骑行中、充电中、故障待维修。一个订单有哪些状态待支付、进行中、待结算、已完成、已取消、争议处理中。把这些状态先列出来数据库表结构基本就定型了。很多人一上来就直接写 controller 和 mapper结果做到一半发现订单状态改不动、车辆状态对不上原因就是前期没有把状态机设计清楚。这个项目我最初设计的核心状态流转是这样的用户登录后在地图上选择空闲车辆提交租车申请生成待支付订单支付押金或完成预授权后下发开锁指令订单变为进行中用户骑行系统按时间计费期间可以上报车辆故障用户还车系统根据骑行时长和计价规则计算费用推送账单用户支付账单后订单完成未支付则进入待结算状态超过一定时间未支付的订单后台可强制关单并记录违约。这个状态机是后续所有接口设计的基准宁可前期多花两天画图也不要后期推倒重来。1.2 技术选型背后的考量技术栈没有悬念地选了 SpringBoot Vue核心原因有三点生态成熟、人才多、中小系统落地成本低。先看后端。SpringBoot 最吸引人的地方是自动装配机制引入一个 starter 就能快速获得一套可用的能力比如 spring-boot-starter-web 内置了 Tomcat、Jackson、参数校验等基础组件spring-boot-starter-data-redis 帮你把 RedisConnectionFactory、StringRedisTemplate 这些 Bean 全部装配好。开发者不需要关心 Tomcat 怎么启动、连接池怎么创建只需要关注业务代码。这也是为什么 SpringBoot 能做“约定优于配置”大多数场景下零配置就能跑起来特殊需求才需要显式覆盖。有人可能会问为什么不选 Spring Cloud 微服务这台电动车租赁系统的业务体量一台 4 核 8G 的服务器就能扛住微服务带来的服务注册、配置中心、链路追踪等组件反而会拖慢交付节奏。单体应用 模块化分包 缓存 异步任务是这个体量下最务实的组合。等到用户量和业务复杂度真的上来再按订单服务、车辆服务、用户服务拆也不迟。技术选型要匹配业务阶段这是我一直坚持的原则。前端选 Vue 的理由同样简单Vue 的渐进式设计让项目可以按需引入特性核心库只聚焦视图层配合 Vue Router 做路由、Pinia 做状态管理、Element Plus 做后台界面组合起来工作效率很高。而且 Vue 的中文社区非常活跃遇到问题搜解决方案的成本很低。其他关键选型数据库MySQL 8.xInnoDB 引擎存订单、车辆、用户等核心业务数据缓存Redis存 Token、验证码、热点车辆状态后续还可以用来做分布式锁地图服务高德地图 Web 端和 JS API覆盖车辆定位、逆地理编码、路径规划文件存储MinIO用来存储用户头像、车辆照片、故障上报图片接口风格RESTful JSON前后端完全分离后端只提供 API。1.3 系统角色与功能模块划分系统按使用者分成三类角色普通用户、运营人员、系统管理员。我前后台各做了一套界面用的是同一套后端 API通过 JWT 中的角色信息做权限控制没有引入 Spring Security 这类重型安全框架而是用拦截器 角色判断这样对中小项目来说成本更低、更容易维护。用户端核心功能注册登录手机号 验证码登录同时绑定用户基本信息车辆地图地图上展示附近可用车辆支持按电量、距离筛选扫码租车扫描车辆二维码查看车辆信息并发起租车骑行中显示实时计费金额、骑行时长、车辆电量、一键上报故障订单中心查看历史订单、当前订单状态、费用明细钱包押金缴纳与退还、余额充值、优惠券。运营端核心功能车辆管理录入新车辆、编辑车辆信息、设置车辆状态空闲/故障/充电订单管理查看所有订单、处理异常订单、人工改价、关单计费规则配置起步价、按时段设置不同单价、设置免费时长用户管理查看用户信息、冻结异常账号数据统计日订单量、营收统计、车辆利用率。这套模块划分基本上把常见租赁系统的业务闭环都覆盖到了后续哪怕要增加功能也只需要在对应模块里扩展不需要大改。2. 后端核心模块设计与实现2.1 项目结构设计后端项目我采用了单一 Maven 工程、按业务分包的结构没有拆多模块。拆分多模块common、system、rental 等适合团队协作或代码量特别大的情况单人或小团队开发时反而增加构建和调试成本。合理做法是包名按业务边界划分未来真要拆也能顺着包边界切分。com.example.rental ├── config # 配置类WebMvc、Redis、CORS、MinIO ├── controller # 接口层只做参数接收与结果封装 ├── service # 业务层核心逻辑与事务管理 ├── mapper # 数据访问层MyBatis-Plus 的 Mapper 接口 ├── entity # 数据库实体类 ├── dto # 请求参数对象 ├── vo # 响应视图对象 ├── common # 统一返回结构、异常处理、常量、枚举 └── utils # 工具类JWT、距离计算、坐标转换Controller 层我只做三件事接收参数、调用 Service、返回统一结果。所有业务判断、事务控制、数据校验都放在 Service 层。Mapper 层只负责 SQL 和简单数据CRUD复杂查询通过 MyBatis-Plus 的 LambdaQueryWrapper 完成不手写 XML 的地方尽量不写。这样代码读起来很干净出了问题也能很快定位是入参问题、业务逻辑问题还是 SQL 问题。2.2 用户认证与权限控制认证方案我直接用了 JWT没有用 Session。前后端分离之后后端 API 不再面向浏览器页面而是面向任意客户端Session 依赖 Cookie 的机制在独立 App 或 H5 场景非常被动。JWT 是把用户身份信息签名后下发给客户端客户端每次请求把它放在 Authorization 头里后端无状态验证服务天然支持横向扩展。登录流程是这样的用户输入手机号和验证码后端校验验证码后用 userId、role 生成一个有效期为 7 天的 TokenRedis 里存一份 Token 与用户状态的映射用于主动踢人。前端拿到 Token 后存在本地每次请求通过 Axios 拦截器自动附加。JWT 工具类核心代码public class JwtUtil { private static final SecretKey KEY Keys.hmacShaKeyFor(your-secret-key-please-change-me.getBytes()); public static String generateToken(Long userId, String role) { return Jwts.builder() .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() 7 * 24 * 3600 * 1000)) .signWith(KEY, SignatureAlgorithm.HS256) .compact(); } public static Claims parseToken(String token) { return Jwts.parserBuilder().setSigningKey(KEY).build() .parseClaimsJws(token).getBody(); } }拦截器部分我自定义了一个 AuthInterceptor在 preHandle 中从请求头读取 Token如果解析失败或过期则直接返回 401同时把 userId 和 role 写入 request attribute后面的 Controller 方法直接取用public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { response.setStatus(401); return false; } try { Claims claims JwtUtil.parseToken(token.replace(Bearer , )); request.setAttribute(userId, claims.get(userId, Long.class)); request.setAttribute(role, claims.get(role, String.class)); } catch (Exception e) { response.setStatus(401); return false; } return true; } }这里有几个细节要注意登录和注册接口必须放行手机号验证码接口要用 Redis 设置过期时间且限制发送频率每次请求都查库获取用户当前状态防止被冻结的用户继续操作。JWT 虽然是无状态的但该做的服务端校验不能省只信 Token 里的内容、不信数据库是一个很容易踩的坑。2.3 租赁核心业务订单状态机与计费订单是整个系统的核心表结构设计之前我先把订单状态机的所有流转条件列了出来。订单状态我用一个字符串枚举字段 status 表示取值包括 PENDING_PAY待支付、RIDING骑行中、PENDING_BILL待结算、COMPLETED已完成、CANCELLED已取消、DISPUTE争议中。核心订单表字段CREATE TABLE rent_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单编号, user_id BIGINT NOT NULL, vehicle_id BIGINT NOT NULL, status VARCHAR(20) NOT NULL, start_time DATETIME, end_time DATETIME, total_amount DECIMAL(10,2) DEFAULT 0.00, pay_amount DECIMAL(10,2) DEFAULT 0.00, deposit DECIMAL(10,2) DEFAULT 0.00, create_time DATETIME, update_time DATETIME, KEY idx_user_id (user_id), KEY idx_vehicle_id (vehicle_id), KEY idx_status (status), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;计费逻辑是这个业务里最容易出 bug 的地方。我用的是阶梯计费规则起步价 3 元包含前 10 分钟超过 10 分钟后每 5 分钟收取 1 元不足 5 分钟按 5 分钟计算。计费规则不写死在代码里而是放在数据库配置表里运营人员可以随时改。还车时的费用计算代码public BigDecimal calculateAmount(RentOrder order) { // 按分钟计算骑行时长向上取整 long minutes Duration.between(order.getStartTime(), order.getEndTime()).toMinutes(); if (minutes 0) minutes 1; BillingRule rule billingRuleService.getCurrentRule(); if (minutes rule.getFreeMinutes()) { return rule.getStartFee(); } long extraMinutes minutes - rule.getFreeMinutes(); // 每 stepMinutes 分钟为一个计费单位向上取整 long units (long) Math.ceil((double) extraMinutes / rule.getStepMinutes()); return rule.getStartFee().add(rule.getUnitFee().multiply(BigDecimal.valueOf(units))); }计费金额一律用 BigDecimal绝不使用 double。double 在金额计算时有精度丢失问题涉及钱的数据类型无脑选 Decimal(10,2)这条经验在电商、租赁、支付系统都适用。状态流转的每一条路径都封装成独立方法比如 startRide、finishRide、cancelOrder、disputeOrder每个方法里先校验当前状态是否允许迁移再更新数据库不允许直接修改 status 字段。这一步是状态机设计的关键能避免很多隐性逻辑漏洞。2.4 地图、定位与骑行轨迹上报地图部分前端用高德地图 JS API 初始化地图展示车辆位置后端负责存车辆经纬度和计算距离。这里要特别注意坐标系问题高德地图用的是 GCJ-02 坐标系也就是俗称的“火星坐标系”如果直接把从高德地图上采集的坐标当作 GPS 坐标处理再叠加其他数据源位置偏差会非常明显轻则几十米重则几百米。所以我的设计原则是所有坐标以后端存储为准前端不管采集还是展示都用高德的坐标体系避免自己去做火星坐标转换。后端只需要把车辆表里的经纬度字段设计为 DECIMAL(10,6)比如 120.153600, 30.287500精度足够支持到米级定位。骑行轨迹我用了一个独立表 ride_track每 10 秒记录一次坐标点CREATE TABLE ride_track ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL, lng DECIMAL(10,6) NOT NULL, lat DECIMAL(10,6) NOT NULL, speed DECIMAL(5,2) DEFAULT 0, report_time DATETIME NOT NULL, KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;用户骑行结束后前端把缓存的轨迹点批量上传后端做轨迹保存和里程计算。如果实时性要求高还可以引入 WebSocket 或 MQTT 做实时位置推送但这个项目里 H54G 网络环境下 10 秒上报一次足够没必要增加复杂度。3. 前端核心页面与交互实现3.1 前端工程搭建前端我用 Vue3 Vite 构建没用 Vue CLI。Vite 的开发服务器启动速度比 Webpack 快一个量级原生 ES Module 的方式让热更新几乎是秒级的开发体验非常舒服。创建项目就直接用官方脚手架npm create vitelatest rental-web -- --template vue cd rental-web npm install依赖我额外装了 Vue Router、Pinia、Axios、Element Plus、高德地图 JS API。目录结构按模块划分src ├── api # 接口请求模块按业务拆分 ├── assets # 静态资源 ├── components # 通用组件 ├── layout # 页面布局 ├── router # 路由配置含守卫 ├── store # Pinia 状态管理 ├── utils # 请求封装、工具函数 └── views # 页面组件Axios 封装是前端开发里最该认真做的一步。我在 utils/request.js 里统一配置了 baseURL、请求超时、请求拦截器附加 Token、响应拦截器统一处理业务码和 401 跳转登录。这样后面所有页面都不用关心 Token 和错误处理代码量直线下降import axios from axios import router from ../router const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 0) { // 未登录或 Token 过期 if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.message)) } return res.data }, error { // 网络错误提示 return Promise.reject(error) } )3.2 车辆地图找车与扫码租车地图找车是用户端最核心的页面。初始化高德地图之后通过后端接口一次性加载附近可用车辆在地图上渲染 Marker。这里重点说一下大数据量下的优化地图上挂几百个 Marker 还不卡但超过上千就会明显掉帧。我用了高德的 MarkerClusterer 聚合插件把同一片区域的车辆聚合显示成一个数字角标放大后自动展开。这个功能在站点密集的场景下效果非常明显。点开车辆 Marker 会弹出详情面板车辆编号、当前电量、时租价格、距我的距离点击“立即租车”会跳转扫码页面。扫码租车这里有个细节扫码本质上是拿到车辆编号而不是真的去调摄像头做识别。H5 页面里我用 input typefile capture 调起原生相机识别二维码后从解析结果里取出 vehicleId再调用后端接口。这里要注意手机兼容性部分安卓机使用 capture 属性会直接打开相机而 iOS 上部分版本会打开相册需要做降级处理比如同时提供手动输入车辆编号入口。3.3 订单流程与实时计费展示骑行中的页面我做了三个模块顶部显示骑行时长和实时费用、中部显示车辆当前行驶信息、底部一个大按钮是“我要还车”。实时费用最初是用 setInterval 每 10 秒向后端拉取一次当前订单状态虽然简单但会产生大量无效请求。后来我改成了“本地预估 后端确认”的方式骑行期间在前端按计费规则定时刷新预估费用还车时再以后端计算为准。这样既保证了用户体验流畅也避免了频繁请求打满后端接口。用户点击还车后前端先上传骑行轨迹再调用后端 finishRide 接口。后端完成费用计算和订单状态变更返回费用明细前端弹出支付确认框。整套流程的状态变化需要和订单状态机严格对齐待支付状态不能骑行骑行状态不能重复还车这些逻辑后端必须兜底。前端只是展示和引导真正的业务约束一定放在后端。4. 关键难点与排查实录4.1 并发下单与车辆状态防冲突租车场景里最容易出现的问题是两个用户同时看到同一辆空闲车几乎同时发起租车申请结果都成功了。本质上是并发下的“重复消费”问题。解决思路就是在更新车辆状态时加上条件只有当前状态为“空闲”的车辆才能被改成“骑行中”。我用的是数据库乐观锁的思路直接写一个带条件的 update SQL而不需要额外加乐观锁版本号字段Update(UPDATE vehicle SET status 1 WHERE id #{vehicleId} AND status 0) int lockVehicle(Param(vehicleId) Long vehicleId);这条 SQL 执行成功后返回影响行数如果返回 0说明车辆状态已经不是空闲要么被别人租走要么已经被锁定直接抛出业务异常提示用户“手慢了车辆已被租走”。这个方法能覆盖绝大多数并发场景实现成本低效果可靠。如果要更严格的防重可以在 Redis 里再加一把分布式锁两个用户同时进来时只有一个能拿到锁去执行下单逻辑。4.2 地图坐标偏移与距离计算做距离排序功能时踩过一个坐标坑一开始用了两个不同数据源的位置信息一个来自高德采集一个是 GPS 原始坐标导致同一个地方在地图上显示的位置和相关距离对不上。排查后确认是坐标系不统一导致的偏差。统一方案是前端所有采集点都使用高德 JS API 拿到的坐标走高德的坐标系后端不做转换。计算两辆车的距离时用 Haversine 公式public static double distance(double lat1, double lng1, double lat2, double lng2) { double earthRadius 6371.0; double dLat Math.toRadians(lat2 - lat1); double dLng Math.toRadians(lng2 - lng1); double a Math.sin(dLat / 2) * Math.sin(dLat / 2) Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2); return earthRadius * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); }Haversine 公式是球面两点间距离计算的标准做法在几公里范围内的误差完全可以忽略。如果你的业务需要更高精度可以考虑用高德的路径规划 API 计算真实骑行距离而不是直线距离但这会增加接口调用成本和响应时间我这边做排序和筛选用直线距离就够。4.3 前后端联调中的跨域与会话问题前后端分离后第一个碰到的问题就是跨域。开发环境前端跑在 5173 端口后端跑在 8080 端口浏览器直接请求必然跨域。开发环境我用 Vite 的 proxy 把 /api 代理到后端前端代码里只写相对路径// vite.config.js server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }生产环境用 Nginx 做反向代理同样是只代理 /api 前缀到后端服务这样前端代码里不用写死接口地址切换环境非常灵活。生产环境的 CORS 配置我也在后端加了兜底允许特定域名跨域访问这样即使前端的接口地址直接指向后端也能通。联调阶段最典型的报错是 401 和 404。401 问题九成是 Token 没传或者过期用浏览器 DevTools 看 Network 面板就能发现 Authorization 头缺失。404 问题主要是路径不匹配后端接口是 POST /api/order/start前端却写成了 POST /api/order/startRide这类问题需要在后端接口设计阶段就统一规范命名并生成接口文档我这边直接用了 Swagger 自动生成文档前后端对照着看省了很多沟通成本。4.4 充电与电量策略电动车租赁不能忽略电量管理。系统里车辆表有 battery 字段用户在骑行过程中电量会变化但真实电量来自硬件 IoT 设备上报在没有硬件的情况下我做了模拟上报后台根据骑行时长和默认功耗曲线定时把电量扣减到车辆状态里。车辆电量低于 20% 时自动把车辆状态置为充电中不再允许新订单运营人员设置充满后重新置为空闲。这里有一个业务细节用户在骑行中电量从 60% 掉到 5%不该让用户在半路抛锚但也不能简单地在电量低于 20% 时强制锁停。我的处理是起租前严格校验电量门槛骑行中则只提醒不打断异常低电量时允许用户上报故障并由运营介入安全细则属于人身安全层面的要求系统只能通过默认规则减少风险真正兜底还得靠运营机制。5. 部署上线与经验总结5.1 Docker 与 Nginx 部署部署我用了 Docker Compose 管理 Nginx、后端容器、MySQL、Redis、MinIO 五个服务。后端 Dockerfile 很简单用多阶段构建把 Maven 打包和运行分开让镜像更小FROM maven:3.9-eclipse-temurin-17 AS build COPY . /app WORKDIR /app RUN mvn clean package -DskipTests FROM eclipse-temurin:17-jre COPY --frombuild /app/target/rental-server.jar /app/rental-server.jar ENV TZAsia/Shanghai EXPOSE 8080 ENTRYPOINT [java, -jar, /app/rental-server.jar]docker-compose.yml 里配置了 MySQL、Redis、MinIO 和后端服务环境变量通过 env_file 加载数据库密码和密钥都不写进代码仓库。前端用 Nginx 做静态托管和反向代理server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location /api/ { proxy_pass http://backend:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }Nginx 配置里最重要的一行是 try_files它把前端路由的所有路径都回退到 index.html否则刷新页面时就会出现 404。这个坑特别经典前后端分离部署时十个人至少有八个会碰到。5.2 个人踩坑与改进方向整个项目做完后回顾踩过的坑最值得写下来的有这几个第一订单号生成不要用自增 ID也不要直接用时间戳。自增 ID 容易暴露业务量时间戳在并发下可能重复。我用的是“前缀 日期 随机串”方式例如 RC20240520153012 4 位随机数虽然简单但完全满足订单号唯一性要求。第二关键的查询字段一定要加索引。订单表 user_id、vehicle_id、status车辆表 station_id、status这些都是高频查询条件。最开始开发阶段数据量小没感觉测试数据一多慢查询马上出现了加上索引后速度立刻上去。第三缓存一致性要重视。车辆状态是热点数据我刚开始用 Redis 缓存车辆状态结果出现后台修改车辆为故障前端地图上车辆仍是空闲的情况。后来简化处理车辆状态永远以数据库为准Redis 只缓存车辆列表的查询结果并设置 30 秒过期。对一致性要求高的数据不要轻易上缓存尤其是强一致场景宁可慢一点也不要脏数据。第四超时订单的自动处理。用户下单后一直不支付车辆就一直被占用。我加了一个定时任务每 5 分钟扫描一次超过 15 分钟未支付的订单把车辆状态重置为空闲、订单置为已取消。定时任务本来想用 xxl-job但为了控制依赖数量直接用 Spring 自带的 Scheduled 就满足了需求。整个项目做完我最深的体会是这种业务系统的复杂度不在某个单独的技术点上而在于状态流转、数据一致性、前后端协作这些“看不见的地方”。正因为如此它才特别适合用来提升全栈能力。如果后续要在这个基础上继续做扩展我会优先考虑增加微信小程序端用 uni-app 复用现有 Vue3 代码再把骑行轨迹改成 WebSocket 实时推送接入地图路径规划获得更准确的里程计费车辆硬件部分接入真实 IoT 上报把模拟电量替换成真实数据。每一个方向都有明确的业务价值这种渐进式的迭代方式也是我在个人项目里最推荐的做法。

相关新闻

基于C# WinForms的MPU6050上位机开发:从串口协议到姿态解算

基于C# WinForms的MPU6050上位机开发:从串口协议到姿态解算

最近手里有块GY-521模块,也就是最常见的MPU6050六轴传感器板子,憋着想做个姿态采集的小项目。可是每次调试都只能开着串口助手盯那堆十六进制数字,数据一变,满屏滚动,根本看不出门道。于是花了一个周末,直接…

2026/10/1 11:16:07 阅读更多 →
微信小程序背景图不显示?本地图片解决方案全解析

微信小程序背景图不显示?本地图片解决方案全解析

做微信小程序时,想给页面加个背景图,第一反应就是给外层view写个 background-image 。我敢打赌,你大概率写过下面这段代码: .container {background-image: url(../../images/bg.png);background-size: cover; }编译没报错&am…

2026/10/1 11:16:07 阅读更多 →
微信小程序背景图不显示?三种必踩坑的WXSS本地图片解决方案

微信小程序背景图不显示?三种必踩坑的WXSS本地图片解决方案

第一次在微信小程序里写 background-image: url(../../images/bg.png) 的时候,我以为这就是一句平平无奇的 CSS,结果开发者工具里一片空白,真机上也是一片空白,左上角倒是能看到小程序正常的标题栏,页面背景就像被谁…

2026/10/1 11:16:07 阅读更多 →

最新新闻

高德API地点搜索与经纬度获取:坐标系转换、配额限流与缓存实战

高德API地点搜索与经纬度获取:坐标系转换、配额限流与缓存实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 20:03:30 阅读更多 →
GPT-5.4实操揭秘:AI Agent如何操作电脑、重塑办公自动化

GPT-5.4实操揭秘:AI Agent如何操作电脑、重塑办公自动化

GPT-5.4发布那天,我在测试环境里跑了整整一下午,最直观的感受就是:这次OpenAI没有挤牙膏。它能打开浏览器、移动鼠标、在网页表单里输入内容、点击按钮,甚至能自己处理异常弹窗——整个过程不需要人盯着,就像给电脑请了…

2026/10/1 20:03:30 阅读更多 →
马德拉岛自由行全攻略:徒步、美食与避坑指南

马德拉岛自由行全攻略:徒步、美食与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 20:03:30 阅读更多 →
单元测试的优雅本质:从契约声明到行为验证

单元测试的优雅本质:从契约声明到行为验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 20:03:30 阅读更多 →
MyBatis SqlSession事务与批量处理:默认不生效的真相与正确姿势

MyBatis SqlSession事务与批量处理:默认不生效的真相与正确姿势

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 20:03:30 阅读更多 →
k8s tips:把 kubeconfig 改到 TaoToken 统一 Key 通道的实操清单

k8s tips:把 kubeconfig 改到 TaoToken 统一 Key 通道的实操清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 20:02:30 阅读更多 →

日新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →