ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

县区就业创业平台开发复盘:小程序+Python+Android三层架构实践

县区就业创业平台开发复盘:小程序+Python+Android三层架构实践 县区级的就业创业服务平台听起来像是个大工程但真正从零做完回头看核心其实就三件事求职者能找到工作、创业者能申报政策、运营人员能高效审核。我负责的这个项目技术栈最后落在微信小程序 Python Android交付仓库代号是_926q2b4。选这三样不是跟风而是县区场景下最现实、最稳的组合。这篇复盘我会把从选型、建模、接口设计到小程序端和Android端实现、再到线上排障的完整过程摊开讲涉及大量可以直接抄走的表结构、接口定义和避坑经验。如果你正在做政务类小程序、地方人才平台、校企对接系统或者类似的需要“C端小程序 管理端App”的项目这篇应该能帮你少走不少弯路。1. 项目整体设计与技术选型1.1 三层技术栈的分工逻辑这个项目最容易被误读的地方是一看到标题里有“Android”就以为要做安卓原生App给求职者用。实际完全不是这样。微信小程序承担的是C端用户入口就业创业服务里的求职者、创业者拿起手机扫一扫就能用不需要安装任何东西。后端接口服务用Python来写负责用户认证、业务逻辑、数据存储和审核流程。而Android端其实是我留给就业服务中心运营人员的移动审核台。为什么用户端不用原生App县域场景下用户的手机配置参差不齐很多人没有装额外App的习惯让他们为了找工作专门下载一个应用转化率非常难看。微信小程序用完即走还能通过公众号、社群、扫码等多种方式扩散对中老年用户更友好。为什么后端选Python而不是Java或Node团队熟悉度是一方面另一个重要原因是FastAPI这套东西开发效率确实高自带OpenAPI文档小程序端和Android端都靠它做接口联调文档自动生成省掉了大量写接口说明的时间。Android端是这个项目里容易被低估的一环。县区就业服务很多工作在线下发生工作人员要去招聘会现场做企业签到、到乡镇街道走访、帮年龄大的求职者代传纸质材料、现场核验企业资质。这些场景抱着笔记本电脑根本跑不动但拿一部Android手机加一个轻量客户端拍照、定位、填表、提交审核一步到位。所以三层技术栈的分工本质是“用户轻、后端快、管理端便携”。1.2 县区场景下的需求池梳理县域就业创业平台和市级以上平台有个明显区别用户群体更复杂业务链条更短但线下服务权重更高。我一开始照着招聘网站的模板设计简历投递、职位搜索做得花里胡哨结果被用户反馈打醒——县里的人用平台就关心三件事有没有离家近的活、补贴怎么申请、培训在哪里报名。最后整理出来的需求池大概是这样的结构业务域核心功能优先级说明就业服务岗位发布与搜索P0企业发布岗位用户按区域、薪资、工种筛选就业服务简历投递P0用户维护简历一键投递支持附件上传就业服务招聘会报名P1线下招聘会活动列表、在线报名、签到码就业服务技能培训P1培训机构入驻、课程发布、报名记录创业服务创业政策展示P0政策原文展示、适用条件拆解创业服务补贴申报P0创业补贴、场地补贴等在线申报与审批创业服务孵化基地导航P2本地创业园、孵化器信息与地图位置管理后台企业与岗位审核P0运营人员审核企业资质、岗位合规性管理后台申报材料审批P0逐级审批申报材料、驳回并填写理由管理后台数据统计P1岗位数、投递数、申报数、审核时效这里有个隐形需求容易被忽略代填帮扶。县里很多求职者年龄偏大手机操作不熟练运营人员需要能“替用户提交”简历或申报材料。这个需求我在Android端做了专门入口后面细讲。1.3 开发路线的先后顺序项目推进顺序是按照“数据先行、接口次之、前端最后”来排的。第一步先把数据库表结构定死第二步用Python把核心接口写完第三步小程序端和Android端并行开发。为什么要这个顺序因为小程序和Android都是接口的消费者接口语义稳定了两头才不会返工。我见过太多项目先做页面再做接口页面做完了发现接口对不上又回头改后端。县区项目周期紧、人手少经不起这种反复。实际操作中我用FastAPI把接口定义好之后直接把自动生成的OpenAPI文档地址发给小程序端开发让他们对着文档自己mock数据调页面这时候后端再去实现细节两边互不阻塞。2. Python后端的骨架搭建2.1 框架与工程结构后端我选了FastAPI SQLAlchemy MySQL Redis这套组合。FastAPI负责路由和参数校验SQLAlchemy做ORMMySQL存业务数据Redis缓存登录态和短信验证码。为什么不用Django县区项目业务体量没那么大Django自带Admin和ORM虽然方便但框架太重前后端分离的模式下很多内置能力用不上。FastAPI的异步支持在接口并发上更从容而且Pydantic的参数校验能省掉大量手工判断。工程结构我是这样的app/ ├── main.py # 应用入口 ├── config.py # 配置读取 ├── database.py # 数据库连接与会话 ├── models/ # SQLAlchemy模型 │ ├── user.py │ ├── enterprise.py │ ├── job.py │ ├── resume.py │ ├── policy.py │ └── application.py ├── schemas/ # Pydantic请求响应模型 ├── api/ │ ├── v1/ │ │ ├── auth.py # 登录接口 │ │ ├── jobs.py # 岗位接口 │ │ └── applications.py # 申报接口 │ └── admin/ │ └── reviews.py # 审核接口 └── core/ ├── security.py # JWT工具 ├── oss.py # 文件存储 └── deps.py # 依赖注入这个结构把普通用户接口和管理员接口从一开始就分开了避免后面权限越界。管理接口统一挂在/admin/api前缀下中间件里单独做管理员身份校验和用户侧JWT是两套逻辑。2.2 核心数据表的字段设计这一块是整个项目的地基设计不好后面全是坑。我挑几个核心表说说。用户表(user)字段名类型说明idBIGINT主键openidVARCHAR(64)微信openid唯一索引unionidVARCHAR(64)公众号unionid可空phoneVARCHAR(20)手机号可空用户未授权时为空real_nameVARCHAR(50)真实姓名id_cardVARCHAR(18)身份证号脱敏存储avatarVARCHAR(255)头像URLroleTINYINT0普通用户1企业用户2运营人员statusTINYINT0禁用1正常岗位表(job)字段名类型说明idBIGINT主键enterprise_idBIGINT所属企业IDtitleVARCHAR(100)岗位名称salary_minINT最低薪资salary_maxINT最高薪资educationVARCHAR(20)学历要求addressVARCHAR(255)工作地址lng / latDECIMAL(10,7)经纬度statusTINYINT0下架1上架audit_statusTINYINT0待审核1通过2驳回view_countINT浏览次数企业表(enterprise)里有个字段得多说一句credit_code是统一社会信用代码这在县区平台里是高频查询字段很多企业用户不知道自己企业在平台注册过没有运营人员经常用信用代码反查。所以这个字段我一律加索引。申报表(application)我用了JSON字段存表单数据因为不同政策类型的申报表单长得不一样用关系表去建模会把简单问题复杂化。JSON存一份、状态机管审核流程既灵活又够用。2.3 接口权限与登录态设计登录态用的是JWT。流程是小程序端wx.login拿到code传给后端的/auth/login接口后端拿code向微信接口换openid签发JWT返回给小程序。用户后续请求带上Token后端解出来就知道是谁了。这里有个细节用户没有授权手机号之前JWT照常签发openid也能标识用户身份但很多业务是不允许未绑定手机号用户操作的。所以我设计了两个token状态一个是临时登录态只允许浏览一个是完整登录态绑定手机号后才能投递简历和申报。这样既不会因为用户拒绝授权就把人挡在门外又能保证关键业务的可追溯性。核心接口列个清单接口方法说明/api/v1/auth/loginPOST微信code换JWT/api/v1/auth/phonePOST绑定手机号/api/v1/jobs/listGET分页岗位列表/api/v1/jobs/{id}GET岗位详情/api/v1/resumePOST保存简历/api/v1/applicationsPOST提交申报/admin/api/v1/reviews/{id}POST审核通过/驳回3. 微信小程序端的关键实现3.1 登录与手机号获取的完整链路微信小程序获取手机号是这次开发中踩坑最多的地方。先讲链路。小程序端先调用wx.login拿code然后请求后端登录接口换token。拿到登录态之后页面里放一个button设置open-typegetPhoneNumber用户点这个按钮会弹窗确认授权allowed之后微信返回一个code把这个code传给后端后端用code换手机号并绑定到用户账号。这里有几个硬性条件提前不说清楚很容易白干。第一个人主体的小程序无法调用getPhoneNumber接口只有企业、政府、媒体等非个人主体才行。第二小程序类目要匹配比如“求职招聘”类目需要对应资质。第三每次按钮点击返回的code只能用一次后端处理时需要做幂等处理防止重复请求导致绑定失败。实操中我还建议做一个“动态手机号验证组件”的降级方案如果某个用户手机型号或微信版本过低导致授权失败可以让用户手动输入手机号再通过短信验证码验证两条路都能完成绑定。政务场景里用户耐心有限不能让他们卡在一个页面里反复失败。3.2 首页信息流、分页加载与顶部导航适配首页是用户第一眼看到的东西信息架构不能太复杂。我的做法是顶部轮播Banner下面是“找工作”“创业政策”“技能培训”“招聘会”四个金刚区入口再往下是推荐岗位列表。岗位列表使用onReachBottom触底加载更多接口传page和pageSize参数后端返回总条数和当前页数据前端把列表拼接起来。这个分页逻辑看着简单但有几个细节必须处理。第一个是加载锁。onReachBottom可能连续触发如果不加isLoading判断会重复请求同一页数据。第二个是数据去重。如果后端排序字段不稳定翻页时可能出现重复数据所以我要求后端排序加一个唯一字段比如order by id desc而不是只按发布时间排。第三个是空态设计。县区岗位总量有限加载完要显示“没有更多了”不能一直转圈。顶部导航栏的适配也值得单独说。小程序默认导航栏在部分安卓机型上会和微信胶囊按钮重叠标题被挡住。我用wx.getMenuButtonBoundingClientRect拿胶囊位置动态计算自定义导航栏高度在iPhone和安卓上分别适配。动态设置标题用的是wx.setNavigationBarTitle比如用户从“岗位列表”进入“岗位详情”后把标题改成岗位名称体验会好很多。3.3 业务表单的校验与附件上传简历投递和创业补贴申报是小程序端表单逻辑最重的两块。简历表单包含姓名、身份证、学历、期望岗位、手机号。身份证是我特别强调校验的字段前端用正则加校验位算法双重检查后端用Pydantic的validator再校验一次双层校验确保脏数据进不了库。移动端键盘类型也必须指定身份证输入框用typeidcard数字输入用typenumber否则用户弹出来的是全键盘输入体验很差。附件上传走的是wx.uploadFile。图片在上传前先用wx.compressImage压缩县区用户用4G网络居多一张原图两三兆传半天还容易失败压缩到200KB以内体验会直线上升。后端接文件后统一存到OSS返回URL存数据库。这里注意一个问题wx.uploadFile的name字段和后端接口接收的字段名必须一致否则后端收不到文件这种问题排查起来特别隐蔽。政策申报我设计成多步骤表单第一步选申报类型第二步填主体信息第三步传证明材料第四步预览提交。分成四步的好处是每步校验范围小用户的注意力不会被一个长表单吓退。提交以后进入审核状态机页面上能看状态流转待审核、已通过、已驳回被驳回时展示驳回理由和可修改的入口不要用户一看到“驳回”就无从下手。4. Android端在项目里做了什么4.1 为什么县区平台需要一个原生Android端前面说了Android端不是给求职者用的是给就业服务中心运营人员用的移动审核台。县区就业服务有一个特点大量工作需要线下完成。招聘会现场要扫码签到、企业入驻需要实地核验营业执照、孤寡老人和不会用手机的群体需要工作人员代填材料。这些场景下工作人员拿着一部安卓手机就能把事办了。Android端在原生的壳里主要做了三件事拍照上传、表单录入、审核操作。很多单位里已经在用企业微信或者内部办公系统所以我这个Android端做了单点登录的对接运营人员用自己的工号登录不用额外记一套密码。拍照上传这里有一个所有Android开发者都会遇到的坑Android 7.0以后如果直接用file:// Uri打开相机拍照会抛FileUriExposedExceptionAndroid 10以后分区存储访问/storage/emulated/0/Android/data目录受限。适配方案是用FileProvider生成content:// Uri并用系统文件选择器或者MediaStore去拿文件。4.2 Android端的技术实现重点Android端的网络层用的是Retrofit OkHttp接口定义和后端OpenAPI文档一一对应。因为涉及到多个图片文件上传比如企业资质要传营业执照、法人身份证、场地照片三张图我在OkHttp里封装了一个带进度回调的MultipartBody每张图片上传时能实时刷新ProgressBar。进度条这个功能看起来小但运营人员现场传图时如果看不到进度会以为App卡死了反复点提交造成重复数据。考虑到县区4G网络有时不稳定我做了上传失败自动重试重试间隔用指数退避最多重试三次。超过三次以后把数据暂存在本地SQLite里等网络恢复后统一上报。这个“离线暂存、联网同步”的机制在招聘会现场特别管用现场人多信号差照片一张张传很容易断先存本地再批量同步成功率大幅提升。WebView在小程序开发里是用来加载协议说明和政策原文的我把政策库做成了一个H5页面Android端用WebView直接加载不需要原生重写一遍展示逻辑。但WebView的坑也不少最典型的是文件下载和打开本地文件需要在WebChromeClient里处理onShowFileChooser回调。Android 9以后默认禁止明文HTTP流量如果H5页面有非HTTPS资源需要在manifest里配置usesCleartextTraffic或者networkSecurityConfig按域名放行。4.3 小程序与Android联调中的抓包实战联调阶段最有用的工具是Charles。开发时我对比过好几款最终日常主力是Charles因为它的断点调试和Map Local功能在开发调试中效率最高。抓小程序的包步骤是手机和电脑连同一个局域网手机WiFi代理指向电脑IP的8888端口再安装Charles的CA证书就能看到HTTPS请求的具体内容。这个操作是纯开发自测用于检查数据格式字段对不对、请求Header带没带Token属于接口联调的基本功。Android 7.0以上有个证书信任问题如果targetSdkVersion 24应用默认不信任用户安装的CA证书会导致Charles解不了密。解决方法是调试包在networkSecurityConfig里加一个debug-overrides只信任用户证书正式包不加。千万别把调试配置带到生产环境否则HTTPS形同虚设。Charles里我最常用的两个功能一个是断点拦截在列表接口上打断点手动修改返回值测试小程序端对异常数据的兼容性另一个是Map Local把某个接口的响应指向本地JSON文件这样前后端联调时后端还没实现前端也能先跑起来。有一说一这个技巧在并行开发时帮了大忙等后端接口真实上线后再把Map Local关掉就行。5. 上线后遇到的常见问题与排查技巧5.1 手机号获取失败的排查思路线上反馈最多的问题就是用户的手机号获取不下来。排查时我一般按这个顺序走先看小程序的AppID对应的主体是不是非个人主体再看是否已经开通了“手机号快速验证”组件能力然后看用户点击时后端有没有收到code回调。如果后端收到了code但解密失败检查AES密钥是否和小程序后台配置一致前后台密钥不一致是特别容易发生的配置错误。另外一个隐蔽原因同一用户在短时间内多次触发手机号按钮微信侧会做频率限制提示“调用频率过快”。我在前端代码里做了按钮防抖用户点击后3秒内禁止二次点击同时在按钮上方给一段小小的提示文案“获取中请稍候”这样既减少无效请求也降低用户因等待焦虑反复乱点的概率。手机号绑定成功后我会把用户资料单独推送到一个消息队列做异步处理不让用户等页面卡顿。5.2 列表触底加载重复请求与卡顿后台工单里有一条是用户反馈“刷着刷着页面就卡住了也不加载新的”还有人说“下滑时突然跳回顶部”。前者一般是分页参数被重复赋值导致的比如page在onReachBottom里被重置成1每次都在请求第一页。后者多半是数据列表没有绑定唯一的key小程序端diff算法混乱导致渲染错乱。岗位列表我用jobId做key这两个问题就一起消失了。还有一个性能和体验问题岗位列表数据量大时一次性把每条岗位的全部字段都返回小程序端渲染就会卡。优化思路是列表接口只返回ID、标题、薪资区间、公司名、缩略图几个关键字段详情内容点进去再请求详情接口。这个“列表轻、详情重”的模式对于低端安卓手机上的小程序性能提升非常明显。县区用户里有很多用千元机不能拿开发机性能去评估线上体验。5.3 Android端文件上传失败的目录与权限问题Android端的文件上传问题主要出在文件路径获取上。很多教程写的是拼接一个/storage/emulated/0/路径这在旧版本安卓上没问题但Android 10以后分区存储直接把这条路堵了除非应用声明了MANAGE_EXTERNAL_STORAGE权限否则拿不到完整访问权。我在适配时把方案改成了用系统文档选择器ACTION_OPEN_DOCUMENT获取Uri这样既不用申请敏感权限也能通过ContentResolver拿到输入流。另一个坑是拍照后再上传时文件丢失。用系统相机拍照返回后Activity可能因为内存不足被系统回收onActivityResult里拿到的Uri变成空。稳妥做法是在onSaveInstanceState里保存Uri字符串在Activity重建后恢复。这套东西不加的话线上会随机出现“拍完照没反应”的反馈很难复现但确实真实存在。5.4 小程序正式版的域名与HTTPS配置开发小程序时可以在开发者工具里勾选“不校验合法域名”这时候请求随便发。但一到真机预览或发布体验版wx.request就会被域名白名单卡住。所以上线前最重要的一件事就是把后端接口域名配到小程序后台的request合法域名列表里。这里有一个坑域名必须是HTTPS而且证书链必须完整缺中间证书会导致部分安卓手机请求失败。我遇到过最诡异的情况是开发工具里一切正常真机上就是请求失败。最后查出来是后端接口的SSL证书只有叶子证书缺少中间证书链微信在真机上校验严格就把它拒了。解决办法是用证书链检查工具看一眼补全中间证书后重新部署问题立刻消失。这个坑如果没人提醒可能要在线上折腾好几天。写在最后整个项目从需求梳理到上线周期大约是四个月其中光联调和排障就占了一个多月。我个人最大的感受是县区就业创业平台最难的不是技术而是把流程理顺——谁能审、怎么审、审完怎么通知用户这些业务规则必须在写代码前定清楚否则后端接口写一半就要推翻重来。最后分享一个小技巧我从一开始就在所有后端接口的响应里加了一个全局字段trace_id每次请求生成唯一ID日志里打印小程序端出问题反馈时用户只要截个图或者发个错误码我就能靠trace_id把整条请求链路捞出来查。别看这只是一个小字段排查远程问题时会帮你省掉大量沟通成本。后面如果想继续扩展这个平台方向也很明确用Python写定时任务把市里公共招聘数据同步下来再做一套简单的可视化统计报表就能让运营人员每周少花半天时间手动整理数据了。
返回列表