尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Flask+微信校园助手:从Token校验到服务器部署完整实战

发布时间:2026/9/29 2:54:11

资讯中心
01
ARTICLE

Flask+微信校园助手:从Token校验到服务器部署完整实战

Flask+微信校园助手:从Token校验到服务器部署完整实战
简介基于Python与Flask构建的微信公共系统校园助手项目面向高校学生及开发者适用于毕业设计、课程设计与项目实训场景。资源以完整可运行的源码为核心涵盖Flask应用模块、HTML页面、JavaScript与CSS前端资源以及两份Markdown部署文档和辅助配置数据。共48个文件压缩包约187KB其中31个Python文件负责业务逻辑与微信接口处理7个HTML页面支撑后台展示整体目录结构清晰便于二次开发。项目经导师指导认可答辩评审95分代码已测试运行成功可直接作为毕设、课设或学习进阶的基础。已有122人学习下载适合希望快速搭建校园助手类微信应用、理解Flask项目分层设计的读者参考扩展。1. 用 Flask 搭微信校园助手先想明白它到底解决什么问题看到“微信公共系统校园助手”这个项目标题先别急着解压源码包。它的本质不复杂一个跑在服务器上的 Flask 后端接收微信公众号服务器转发的用户消息查校园数据再把结果以文本或图文形式回复给用户。用户在微信里看到的是一个会查课表、报失物的公众号开发者眼里其实就是一条 HTTP 路由加几张数据表。这种“高分项目”的价值不在压缩包本身而在于你能不能把它跑起来、讲清楚并且换台机器还能重新部署。适合做课程设计、毕业设计的在校学生也想给学院公众号写自动回复的同学。下面我按真实项目的落地顺序——握手、功能、部署、避坑——拆开讲。2. 微信公众平台的握手逻辑先过 token 校验再谈消息收发很多新手拿到 Flask 代码就跑去公众号后台填 URL结果永远卡在“配置失败”。问题在于没搞懂微信的握手机制公众号后台和你的服务器之间不是直接连的微信需要先确认这个 URL 背后确实是你。这个确认动作叫服务器 URL 校验它是 GET 请求而真正的用户消息是 POST 请求。两套逻辑混在一起写是第一个翻车点。2.1 服务器配置里 URL、Token、EncodingAESKey 各在管什么打开公众号后台的“设置与开发 → 基本配置”你会看到三个必填项参数作用新手建议URL微信服务器回调你的接口地址用户消息会 POST 到这里形如http://你的域名/wechatToken你自定义的字符串相当于校验用的共享密钥随便写一串随机字符越乱越好EncodingAESKey消息加密模式下的密钥先用明文模式跑通后再研究加密当你点击“提交”时微信会往这个 URL 发一个 GET 请求带上signature、timestamp、nonce、echostr四个参数。你的服务器要做的是把Token、timestamp、nonce三个字符串按字典序排序、拼接、做 SHA1 哈希得到的值如果等于signature就把echostr原样返回。微信收到正确的echostr才认为这个 URL 是你的之后才会把用户消息 POST 过来。提示微信公众号后台的服务器 URL 只支持 80 和 443 端口本地调试时不要指望微信能访问你笔记本上的 5000 端口。2.2 最小 Flask 路由跑通 URL 校验SHA1 签名的计算顺序先用最精简的代码把校验跑通这是整个项目的“门卫”。网上流传的代码各有写法但核心就三步排序、拼接、哈希。import hashlib from flask import Flask, request app Flask(__name__) # 和公众号后台填写的 Token 保持一致改一个字符都校验不过 TOKEN campus_helper_2024 app.route(/wechat, methods[GET, POST]) def wechat(): if request.method GET: # 微信服务器配置提交时发的是 GET 校验请求 signature request.args.get(signature) timestamp request.args.get(timestamp) nonce request.args.get(nonce) echostr request.args.get(echostr) # 1. 三个参数放进列表 tmp_list [TOKEN, timestamp, nonce] # 2. 字典序排序这一步顺序错了哈希永远对不上 tmp_list.sort() # 3. 拼成字符串再做 SHA1 tmp_str .join(tmp_list) if hashlib.sha1(tmp_str.encode(utf-8)).hexdigest() signature: # 校验通过原样返回 echostr注意是字符串 return echostr return verify failed, 403 # POST 分支留给消息处理下一节写 return ok if __name__ __main__: app.run(host0.0.0.0, port80)这段代码看着短有两个容易忽略的点。第一sort()是 Python 默认的字典序微信那边也是同样的排序规则两边的排序结果必须完全一致所以别手动拼接直接让sort()处理。第二返回echostr时不要加引号、不要格式化微信拿它做严格比对多一个空格都会失败。为什么用 SHA1因为微信在早期就定了这套签名协议至今没变你只需要照做。2.3 POST 路线把微信的 XML 包解析成校园业务能用的数据校验通过后用户在公众号里发的每条消息都会以 POST 形式转发过来请求体是一段 XML。比如用户发了一句“查课表”你收到的原始数据长这样xml ToUserName![CDATA[gh_xxxxxx]]/ToUserName FromUserName![CDATA[oABCDEF123]]/FromUserName CreateTime1720000000/CreateTime MsgType![CDATA[text]]/MsgType Content![CDATA[查课表]]/Content MsgId2400000001/MsgId /xmlFromUserName是用户的 openidToUserName是公众号的原始 IDContent是用户输入的文本MsgId是这条消息的唯一编号后面做去重靠它。解析之后要拼一段新的 XML 回去注意收发双方要互换import time import xml.etree.ElementTree as ET from flask import request, make_response app.route(/wechat, methods[GET, POST]) def wechat(): if request.method GET: # 校验逻辑同上省略 pass # 解析微信转发的 XML xml_data request.data root ET.fromstring(xml_data) from_user root.findtext(FromUserName) # 用户 openid to_user root.findtext(ToUserName) # 公众号原始 ID msg_type root.findtext(MsgType) content root.findtext(Content) # 按消息类型分发给校园助手的业务模块 if msg_type text: reply_text handle_campus_biz(content, from_user) reply build_text_reply(from_user, to_user, reply_text) else: reply build_text_reply(from_user, to_user, 暂不支持这类消息) resp make_response(reply) resp.content_type application/xml; charsetutf-8 return resp def build_text_reply(from_user, to_user, content): 构造文本回复 XML注意 ToUserName 和 FromUserName 互换 reply fxml ToUserName![CDATA[{from_user}]]/ToUserName FromUserName![CDATA[{to_user}]]/FromUserName CreateTime{int(time.time())}/CreateTime MsgType![CDATA[text]]/MsgType Content![CDATA[{content}]]/Content /xml return reply这里有三个新手必踩的细节。第一回复 XML 里ToUserName要填用户的 openidFromUserName要填公众号的 ID跟收到的消息正好相反写反了微信会提示“该公众号提供的服务出现故障”。第二CreateTime用 Unix 时间戳int(time.time())就够了。第三所有用户输入的内容必须包在![CDATA[]]里否则用户发个或就直接把 XML 搞坏了。到这一步你的 Flask 服务已经具备和微信公众号对话的资格接下来才轮到业务功能。3. 把校园助手功能做厚课表查询与失物招领的落地写法握手通了接下来是这个项目的重头戏校园业务。市面上这类“校园助手”源码包功能再花哨核心也就几块——课表查询、成绩查询、失物招领、校园通知。这里我挑课表和失物招领两块展开因为它们代表了两种典型写法一个查自己的数据一个搜公共的数据池。3.1 SQLite 轻量化存储课表和失物招领两张表就够先说存储选型。很多课程设计一上来就上 MySQL但校园助手这种场景用户量几百到几千数据量撑死几万行用 SQLite 完全够。SQLite 是单文件数据库零配置备份直接把.db文件拷走部署时不用额外装服务这对 Flask 项目来说省掉一大半运维成本。我一般在项目根目录建一个init_db.pyimport sqlite3 def init_db(): conn sqlite3.connect(campus.db) cur conn.cursor() # 课表按学生 openid 存weekday 1-7 表示周一到周日 cur.execute( CREATE TABLE IF NOT EXISTS course ( id INTEGER PRIMARY KEY AUTOINCREMENT, openid TEXT, course_name TEXT, weekday INTEGER, start_section INTEGER, end_section INTEGER, location TEXT ) ) # 失物招领kind 区分失物还是招领title 和 description 存描述 cur.execute( CREATE TABLE IF NOT EXISTS lost_found ( id INTEGER PRIMARY KEY AUTOINCREMENT, kind TEXT, title TEXT, description TEXT, contact TEXT, create_time TEXT ) ) conn.commit() conn.close() if __name__ __main__: init_db()这段设计决定了后续所有查询逻辑的形态。course表用openid做用户维度是因为微信不给你用户手机号openid 就是用户在公众号里的唯一身份。weekday和start_section、end_section分开存是为了支持“今天星期几上什么课”这种动态查询而不是只存一个“周一第一大节”的字符串。lost_found表的kind字段是关键失物和招领是两种方向用户发“我丢了钥匙”和“我捡到一串钥匙”匹配逻辑完全不同。3.2 课表查询与失物招领的关键词路由从精确匹配到相似度匹配业务函数handle_campus_biz的写法决定了助手的“智商”。最朴素的版本就是字符串判断def handle_campus_biz(content, openid): content content.strip() if content.startswith(课表): return query_course(openid, content) if 失物 in content or 招领 in content or 丢 in content: return search_lost_found(content) return 试试这样问我\n课表\n失物招领课表查询相对直接用户发“课表”就查全部发“课表 周一”就按 weekday 过滤import sqlite3, datetime def query_course(openid, content): conn sqlite3.connect(campus.db) cur conn.cursor() # 如果没指定星期默认查今天 if 周 in content: weekday int(content[-1]) # 取“周一”里的 1 else: weekday datetime.datetime.now().isoweekday() cur.execute( SELECT course_name, start_section, end_section, location FROM course WHERE openid? AND weekday?, (openid, weekday) ) rows cur.fetchall() conn.close() if not rows: return f周{weekday}没有课 return \n.join(f{r[0]} 第{r[1]}-{r[2]}节 {r[3]} for r in rows)注意这里用了参数化查询?别用 f-string 拼 SQL既防注入也避免引号转义的坑。失物招领要复杂一些因为用户说“找钥匙”你的数据里可能写的是“一串钥匙落在三教”这需要相似度匹配而不是完全相等。这里有个热门的做法是参考“基于 Flask 的校园失物招领智能匹配平台”的思路用中文分词加集合交集做关键词精准匹配def search_lost_found(content): conn sqlite3.connect(campus.db) cur conn.cursor() cur.execute(SELECT kind, title, description, contact FROM lost_found) rows cur.fetchall() conn.close() # 用 jieba 分词把用户输入拆成关键词集合 import jieba query_words set(jieba.cut(content)) results [] for kind, title, desc, contact in rows: text f{title}{desc} title_words set(jieba.cut(text)) # 交集词数就是相似度分数 score len(query_words title_words) if score 0: results.append((score, kind, title, desc, contact)) results.sort(keylambda x: x[0], reverseTrue) if not results: return 暂时没有匹配的失物招领信息 # 取前 3 条返回 lines [] for score, kind, title, desc, contact in results[:3]: tag 招领 if kind found else 寻物 lines.append(f[{tag}]{title}\n{desc}\n联系{contact}) return \n\n.join(lines)jieba是中文分词库pip install jieba就能装分词后取交集交集越大匹配度越高。这个方案比正则匹配聪明在用户说“钥匙丢了”数据里写“一串钥匙”分词后都有“钥匙”这个词就匹配上了而纯字符串in判断会因为“丢了”和“钥匙”的顺序问题漏掉。匹配精度的调参主要看分词的粒度jieba.cut默认模式对校园场景够用如果你发现误匹配太多可以换成jieba.lcut再手动加停用词表过滤“的、了、吗”这类无意义词。3.3 图文消息回复让查询结果从一行字变成可点击卡片文本回复够用但体验一般。微信公众号支持图文消息news 类型一屏能展示标题、描述、封面图和跳转链接。校园助手最常见的做法是失物招领结果用图文卡片点进去看详情。构造图文回复的 XML 和文本略有不同def build_news_reply(from_user, to_user, articles): articles 是列表每个元素是 {title:..., description:..., pic_url:..., url:...} items for a in articles: items fitem Title![CDATA[{a[title]}]]/Title Description![CDATA[{a[description]}]]/Description PicUrl![CDATA[{a[pic_url]}]]/PicUrl Url![CDATA[{a[url]}]]/Url /item reply fxml ToUserName![CDATA[{from_user}]]/ToUserName FromUserName![CDATA[{to_user}]]/FromUserName CreateTime{int(time.time())}/CreateTime MsgType![CDATA[news]]/MsgType ArticleCount{len(articles)}/ArticleCount Articles{items}/Articles /xml return reply图文消息有两个隐藏要求。第一PicUrl和Url必须是公网可访问的完整地址不能填相对路径否则卡片显示空白开发阶段可以先用一个占位图地址顶上。第二ArticleCount必须和item数量一致写错数字微信会直接丢弃整条回复。你要是把失物招领的查询结果转成这种卡片格式整个项目的完成度立刻上一个档次这也是“高分项目”和普通作业的明显分水岭。4. 云服务器上的 Flask 部署systemd 托管 Nginx 反向代理组合拳功能写完了下一步是把项目部署到服务器上。说到底公众号要求你的 URL 必须公网可达这一步躲不开。这里说的部署方案是我个人最常用的组合云服务器上装 Python 环境Systemd 让 Flask 常驻Nginx 做反向代理统一收流量。整个过程按顺序做半小时能跑通。4.1 服务器环境准备Python 3 venv 虚拟环境 依赖安装拿到源码包后的第一件事永远是看依赖清单。正规项目会带一个requirements.txt里面列着flask、requests、jieba这些包。先建虚拟环境再装依赖这是避免把服务器系统 Python 环境搞乱的后悔药# 以 Ubuntu 为例先更新系统并安装基础组件 apt-get update apt-get install -y python3 python3-venv nginx # 建项目目录并进入 mkdir -p /opt/campus_helper cd /opt/campus_helper # 把源码包解压到当前目录假设压缩包已上传到服务器 unzip campus_helper.zip -d . # 创建虚拟环境 python3 -m venv venv source venv/bin/activate # 安装依赖国内服务器建议加清华镜像源速度快很多 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple如果你的源码包没有requirements.txt也不要慌看app.py顶部的import语句缺什么装什么flask和requests是跑不掉的。这里有个细节生产环境不要用python app.py直接跑Flask 自带的开发服务器性能差换个思路用gunicorn这个 WSGI 服务器来托管 Flask 应用它支持多 worker 并发扛几百个公众号用户毫无压力。4.2 用 systemd 把 Flask 服务变成常驻进程gunicorn装好后手动启动没问题但服务器一重启服务就没了这不叫部署。正确做法是写一个 systemd 服务单元文件让系统帮你管理进程崩溃自动拉起开机自动启动。在/etc/systemd/system/campus_helper.service里写[Unit] DescriptionCampus Helper Flask Service Afternetwork.target [Service] Userwww-data WorkingDirectory/opt/campus_helper # 2 个 worker 进程绑定本机 5000 端口不直接暴露公网 ExecStart/opt/campus_helper/venv/bin/gunicorn -w 2 -b 127.0.0.1:5000 app:app Restartalways RestartSec3 [Install] WantedBymulti-user.target写完后依次执行三条命令systemctl daemon-reload systemctl enable campus_helper # 开机自启 systemctl start campus_helper systemctl status campus_helper # 查看运行状态ExecStart里app:app的含义是从app.py文件里找名为app的 Flask 实例。如果你的主文件叫main.py就要写成main:app。-w 2是 worker 进程数通常设为 CPU 核数的两倍即可不要盲目加大因为每个 worker 都会占用内存。Restartalways是血泪经验公众号接口偶发超时导致 gunicorn worker 异常退出没有这个配置服务就静默死掉了用户发消息永远没人回。4.3 Nginx 反向代理让公众号只认 80 端口Flask 服务现在监听在127.0.0.1:5000公网访问不到。公众号后台要求 URL 走 80 或 443 端口所以用 Nginx 把 80 端口的请求转发给内网的 5000 端口。在/etc/nginx/sites-available/campus_helper里写server { listen 80; server_name your_domain.com; # 换成你自己的域名 location /wechat { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }然后启用这个配置并重载ln -s /etc/nginx/sites-available/campus_helper /etc/nginx/sites-enabled/ nginx -t # 先验证配置语法有错别急着重载 systemctl reload nginx这样公众号后台的 URL 就填http://your_domain.com/wechat。proxy_set_header那几行不是可选项微信的回调请求会带上原始 HOST 头不转发的話 Flask 里取到的域名是127.0.0.1:5000后续做消息回复里的跳转链接时全部会拼错。到这一步你的服务已经具备上线条件。4.4 部署后的自测用 curl 模拟微信的校验请求部署完别急着去公众号后台点提交先用 curl 在服务器本机模拟一次微信校验这时候出错成本最低。先算出一个合法签名python3 -c import hashlib; print(hashlib.sha1(.join(sorted([campus_helper_2024,1720000000,123456])).encode(utf-8)).hexdigest())再把算出来的签名拼进 URL 请求curl http://127.0.0.1/wechat?signature上面算出的值timestamp1720000000nonce123456echostrhello如果一切正常curl 会原样返回hello。如果返回 403按这个顺序排查Token是否和公众号后台一致、timestamp和nonce是否和你算签名时用的完全一致、TOKEN字符串有没有空格。本机通了再去公众号后台提交配置基本一次过。之后想看实时日志用journalctl -u campus_helper -f跟踪所有print输出都会出现在这里。5. 微信校园助手避坑指南5 个高频翻车现场与排查顺序这部分是付费都未必有人教的踩坑合集全是真实项目里反复出现的翻车现场。每一条我都按“现象 → 原因 → 解决”写清楚你对照着排查能省下大半天。5.1 服务器 URL 校验一直失败先查排序和 Token 一致性现象公众号后台点“提交”提示“配置失败”检查了好几遍 URL 和 Token 都没写错。原因绝大多数情况是签名计算顺序不对。微信的签名规则是先把Token、timestamp、nonce三个字符串放进列表按字典序sort()然后再拼接做 SHA1。有人手写排序逻辑有人拼接时把Token放在了最后哈希值就对不上。另一种可能是服务器代码里TOKEN变量末尾多了换行符或空格肉眼看不出来但哈希对不上。解决把签名计算单独抽成一个函数加日志打出来对比。在本地把signature、timestamp、nonce三个值打出来和公众号后台生成的请求参数逐字比对重点看末尾有没有空白字符。另外本地调试时别用微信后台触发用 4.4 节的 curl 方法自己造请求调通了再让后台来校验。5.2 消息处理超过 5 秒微信直接把回复丢掉现象后端日志显示已经处理了用户消息回复也拼好了但用户那边始终看不到任何回应。原因微信公众平台要求被动回复必须在 5 秒内返回。如果你的处理逻辑里有慢查询、外部 API 调用、或者图片下载很容易超时。超时后微信会重试同一个请求最多重试 3 次每次都超时的话这条消息就彻底丢了。解决把重活拆出去。常见做法是收到消息后先立刻返回一个空字符串合法的被动回复同时把任务丢进后台线程去处理最后通过客服消息接口主动推送给用户。另一种更轻的办法是缩短处理链课表查询这种数据库操作控制在 5 秒内通常没问题但像“联网查天气再回复”这种就别塞进被动回复里。给你的每个业务函数加耗时统计超过 1 秒就要警惕。5.3 同一条消息被处理两次失物招领数据写重了现象用户发布一条失物信息后台表里出现了两条一模一样的记录。原因微信对 5 秒内没有正常回复的消息会自动重试重试的请求里带着同一个MsgId。如果你的处理逻辑没有做幂等控制第二次请求进来时会把同一条数据再插一遍。还有一种情况是你自己在超时后手动补发回复又把处理逻辑跑了一遍。解决用MsgId做唯一性约束。在失物招领表里加一列msg_id插入前先查一下存在不存在或者直接给msg_id加唯一索引。更通用的做法是维护一张msg_log表记录所有收到过的MsgId和处理时间每次请求先查这张表存在就跳过。这也是我强烈建议每个公众号项目都做的事后面排查问题全靠它。5.4 中文乱码和 XML 解析报错charset 和 CDATA 缺一不可现象用户消息里含中文时解析出来是乱码或者用户发了个表情符号ET.fromstring直接抛异常。原因第一Flask 返回响应时没有指定content_type默认可能不是 UTF-8 编码微信收到后按错误编码解读就变乱码。第二用户消息内容里含有、、之类的 XML 特殊字符没有用CDATA包裹解析器直接报错。解决所有make_response都显式声明resp.content_type application/xml; charsetutf-8。所有动态内容用户名、消息正文、查询结果在拼 XML 时一律包进![CDATA[]]不要偷懒用转义函数替代。另外解析时用ET.fromstring(request.data)不要用request.get_data(as_textTrue)再手动编码request.data是原始字节流交给 ElementTree 处理编码更稳。5.5 Windows 上跑得好好的传到 Linux 服务器就找不到附件文件现象本地开发时用户发的图片能正常保存和回复部署到服务器后报“FileNotFoundError”或者图片 404。原因这是 Windows 和 Linux 路径差异的经典翻车。“高分项目”里常见代码写的是D:/uploads/xxx.jpg这种带盘符的绝对路径或者uploads\xxx.jpg这种反斜杠路径。Windows 下能跑换到 Linux 服务器上路径不存在目录还不能写。解决路径统一用os.path.join拼不要手写斜杠。文件存储目录用相对app.root_path的路径启动时先os.makedirs建目录。比如UPLOAD_DIR os.path.join(app.root_path, uploads)确保目录存在并给予写权限。如果 Nginx 也参与图片服务要单独配一个location /uploads指向真实目录否则 Flask 返回的图片 URL 会被 Nginx 拦下来。排查时先ls服务器上的实际文件路径再对比代码里拼出来的路径十有八九是少了一层目录。6. 进阶玩法从被动回复到主动推送做一个会找人的校园助手被动回复只是公众号最基础的能力。真正让校园助手“活”起来的是主动推送——比如有人发布了一条“在三教捡到校园卡”的招领信息系统自动把这条消息推给所有发布过寻物启事的用户。这个功能的骨架是自定义菜单加模板消息代码量不大但需要你理清一个关键前提微信公众号的接口调用需要access_token。6.1 自定义菜单把文本口令变成按钮用户不用记“课表”“失物”这些口令直接点菜单就行。创建菜单是主动调微信接口你只需要在 Flask 里加一个初始化路由import requests def create_menu(access_token): url fhttps://api.weixin.qq.com/cgi-bin/menu/create?access_token{access_token} menu_data { button: [ {type: click, name: 今日课表, key: COURSE_TODAY}, {type: click, name: 失物招领, key: LOST_FOUND}, ] } resp requests.post(url, jsonmenu_data) print(resp.json()) # {errcode: 0, errmsg: ok} 才算成功key值会在用户点击菜单时以event消息 POST 到你的服务器在MsgType event分支里按EventKey分发就行逻辑和文本消息完全一致。6.2 用 access_token 推模板消息把失物招领主动送到用户手里access_token是调用所有微信接口的通行证有效期 7200 秒获取接口本身需要你的appid和appsecret。注意这里的坑access_token必须缓存起来频繁调用获取接口会被微信限流。import time, requests class TokenManager: def __init__(self, appid, secret): self.appid appid self.secret secret self.token None self.expires_at 0 def get_token(self): # 提前 5 分钟过期留出刷新余量 if self.token and time.time() self.expires_at - 300: return self.token url https://api.weixin.qq.com/cgi-bin/token resp requests.get(url, params{ grant_type: client_credential, appid: self.appid, secret: self.secret }).json() self.token resp[access_token] self.expires_at time.time() resp[expires_in] return self.token拿到 token 后推模板消息先要在公众号后台申请模板拿到模板 ID然后拼一个touser用户 openid加data的 JSON 发出去。模板消息最适合的场景就是失物招领成功匹配后的即时提醒。我现在的固定习惯是不管项目多小先建一张msg_log表记录每条收发消息和每次接口调用的响应码出问题先查日志再动代码。这个习惯帮我躲过了无数次“黑匣子”式的排查。校园助手这个方向技术上不复杂但把握手、部署、去重、推送这些细节都理顺它就是一个能真正跑起来服务学生的完整系统。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。