lovife
|
2ff17cd0c9
|
refactor(h5)!: H5 改由 nginx 托管,后端退回纯 API
用户决定:后端不再对外发静态文件,学生端 H5 交给网关。两者**仍然同源**
(微信只给一个授权回调域名;H5 的接口地址取自 location.origin),
所以前端业务代码、微信后台、application-dev.yml 的 h5-base-url 都不用改。
后端
- 删掉 dev/test/prod 三份 profile 里的 spring.web.resources.static-locations
- 主配置加 spring.web.resources.add-mappings=false —— 用开关而不是"指到空目录":
后者会连带启用 Spring Boot 的默认位置(classpath:/static 等),谁丢个文件进去就悄悄对外可见
- StaticCacheHeaderFilter → ApiNoStoreFilter,只剩 /api/** 的 no-store
(静态资源的缓存策略整体归 nginx,避免"改一处漏一处")
- 补 NoHandlerFoundException → 404:关掉静态映射后未匹配路径不再经过
ResourceHttpRequestHandler,抛的是 NoHandlerFoundException,只处理
NoResourceFoundException 时访问 / 会变成 500「系统繁忙」并刷 ERROR 日志
deploy
- 新增 xt-nginx(80):发 frontend-mp/dist/build/h5 + /api 反代 8080(host.docker.internal,
Linux 靠 extra_hosts host-gateway),端口与产物目录走 .env 的 H5_PORT/H5_DIR
- 三条"删了就会出事"的都已带上理由:client_max_body_size 50m(默认 1m,超了回 413
而后端日志一个字都没有)、/assets 的 immutable 与 *.html 的 no-cache(§一·十 那个
"发版后学生看到旧页面"的坑)、健康检查写 127.0.0.1(容器里 localhost 先解析到 IPv6,
nginx 只听 IPv4 → 永远 starting 而首页是好的)
CI 与文档
- CI:学生端 H5 构建从"e2e 的输入"改为编译检查,不再留 artifact(e2e 不消费它)
- 同步 AGENTS.md / README.md / docs/09(启动步骤、CI 表、发布注意、排障表)/ docs/11
- 交接摘要新增 §一·四十三(含理由、验证数字、要人工做的一步与环境限制)
验证
- mvn clean package 通过(必须 clean:删掉的旧类会留在 target/classes 被组件扫描)
- 后端直连:/ 与 /assets/* → 404({"code":40001,"msg":"接口不存在"})、
/api/.../captcha → 200 + no-store、/doc.html → 302→200、/v3/api-docs 与 /actuator/health → 200
- 经 nginx(80):首页 200 + no-cache、/assets/*.js 200 + immutable、/nope.js 404(无回退)、
/api 200 + no-store、gzip 生效;2MB multipart 上传 → 401(与直连一致 ⇒ 未踩 1m 的坑)、
55MB → 413
- e2e(XT_BASE=http://localhost,全程走 nginx):e2e-m0.py 37/37、e2e-m3-h5-auth.py 24/24、
e2e-m2-scope.py 38/38
- verify-all.py --quick 6/6;check-test-profile.py:test profile 仍能起来
|
2026-10-19 17:35:33 +08:00 |
|
lovife
|
c8837ee602
|
feat(file): 上传预检/秒传/批量处理/预览 + 缩略图(P2-7)与 H5 独立 dev server(P2-9)
缩略图:StorageService.thumbUrl(path, expire, maxLongSide),七牛在**签名之前**拼
imageView2/2/w/h(拼在签名之后必 401),本地存储回退原图,让调用方不必判断当前是什么存储。
实测 1454KB → 36KB(2.4%)、1600×1200 → 400×300。
F-73:秒传原来排在份数检查之后 → 网络重传得到「你超了」而不是「你已经传过了」。
H5 dev server:vite 加 /api 代理 + .env.development 把 VITE_API_BASE 设空串,
改一行不用再 build:h5;三端行为不同(dev→代理 / 构建产物→location.origin / 小程序→写死)。
验证:新增 e2e-m9-thumb.py 12/12(打到真实七牛)、e2e-m8-orgfile 54/54;
H5 与小程序两端构建都验过。
|
2026-09-30 09:02:56 +08:00 |
|
lovife
|
2659cd5a8c
|
feat(h5): H5 交给后端托管 + 修掉"相对地址在 uni.request 下失败"
背景:配好了花生壳穿透 http://394nb68658le.vicp.fun → 本机 8080。
但 H5 原来跑在 127.0.0.1:5174,手机访问不到 —— 所以把 H5 静态文件
交给后端一起服务(同一个域名)。这样顺带也不再需要 CORS,
而且正是正式部署的形态。
改动:
· application-dev.yml 加 spring.web.resources.static-locations,
默认指向 frontend-mp/dist/build/h5/,可用 H5_STATIC_DIR 覆盖。
⚠️ 前缀必须是 spring.(我一开始写成了 fill.,表现是"页面 404 而应用
照常启动、没有任何报错")。
· fill.mp-h5.callback-url / h5-base-url 改成穿透域名。
· config/index.ts:API 地址按端区分(api 地址两端策略不同,写死一个值
一定会在某一边出错):
H5 → location.origin(绝对同源)
小程序 → http://localhost:8080(没有"同域"概念,必须绝对地址)
用 uni-app 条件编译区分,而不是运行期判断(后者会把两套地址都打进包里)。
⚠️ 修掉一个只在真实穿透下才会暴露的 bug:
· 原来 H5 硬编码 http://localhost:8080。从公网域名打开时变成跨域请求 → 被 CORS 拦
→ 页面显示"网络请求失败",而**从本机打开却是好的**。这个 bug 只有走真实
穿透/真机才会暴露 —— 这正是"要在真环境验证"的价值。
· 改成相对路径后**仍然失败**(request:fail),但同样地址用原生 XHR / fetch
都是 200。也就是说这是 uni.request 自身对相对路径的处理差异,不是服务端
或跨域问题 —— 而它长得像"网络不通",很容易往 CORS / 域名 / 后端去查,白绕一大圈。
最终用 location.origin(绝对同源)绕开。
验证:经公网 http://394nb68658le.vicp.fun/?scene=t1-k17
→ 显示「示范职业学校 / 2026年9月机电专业认定材料收集 / 尚未发布」,
无头浏览器截图确认(不依赖人工)。
※ 首次加载约 27 秒 —— 穿透的带宽限制(花生壳免费档),不是代码问题。
|
2026-09-29 11:11:49 +08:00 |
|
lovife
|
0bc7ee02dc
|
feat(mp): 小程序端骨架 + 扫码落地页(scene 识别租户)
小程序端从零建起来了,并在微信开发者工具里真机模拟跑通:
扫 scene=t1-k17 → 显示「示范职业学校 / 2026年9月机电专业认定材料收集 / 尚未发布」。
交付:
· frontend-mp/(uni-app,官方 uni-preset-vue#vite-ts 模板),appid 已填
· src/pages/index/index.vue 扫码落地页:读 scene → 调后端 → 显示
「哪个学校 / 填什么 / 现在能不能填」
· GET /api/v1/mp/scene-context(匿名):登录前的落地页查询
· 主题色与 PC 管理端同一套(腾讯云 TDesign 色板),两端视觉一致
为什么落地页必然是匿名接口:
学生扫开的第一屏要回答"这是哪个学校、要填什么、能不能填",
而这发生在绑定身份之前。它与契约里的 POST /mp/login(code+scene 换 session)
不是一回事 —— 那个要换 openid,属 M3。
⚠️ 一个必踩的坑(撞上并绕开了):
FillTenantLineHandler.getTenantId() 在**匿名请求下直接抛 40101**,
所以不能用 taskMapper.selectById()。正确写法是专用 Mapper + 每条查询
@InterceptorIgnore(tenantLine="true") 并显式带 tenant_id(项目注释称此为
一次代码审查点)。见 module/mp/mapper/MpSceneMapper.java —— 把这几条集中在
一个文件里,正是为了让"关掉框架保护后由人保证隔离"这件事有地方审。
⚠️ scene 可穷举(t1-k1、t1-k2…),所以匿名接口:
① 只返回落地页必须的最少信息,不往里加"顺带能查出来"的东西;
② 按 IP 限流(20 次/分钟,Redis INCR,复用项目的 GATE_RATE_LIMITED 40071)。
E2E 专门验了"前 20 次通过、第 21 次起被限、清掉计数器立刻恢复"。
状态文案分五种而不是合并成"当前不可填报":学生的下一步动作完全不同 ——
「已截止」要找班主任、「已暂停」要等通知、「尚未发布」是根本不该现在扫。
顺带记两个环境坑(写进交接摘要):
· 微信开发者工具的「服务端口」默认关闭,HBuilderX 运行时报
`IDE service port disabled` 然后退出,**调不起开发者工具**。
解法:开发者工具 → 设置 → 安全设置 → 服务端口 → 开启;
或直接「导入项目」dist/dev/mp-weixin 绕过。
· HBuilderX 把这个项目当 CLI 型项目,产物在 dist/dev/mp-weixin,
**不是** unpackage/。模拟扫码要在「编译模式」里设 进入场景=扫描二维码、
启动参数 scene=t1-k17。
· <style lang="scss"> 需要装 sass(模板没带),已 pnpm add -D sass。
⚠️ 对上线计划最重要的一条结论(已写进交接摘要):
开发版(develop)只有开发者能扫、且会过期;体验版(trial)只有体验成员能扫;
**只有正式版(release)学生才能扫**,而它需要提交审核,涉及小程序类目与资质。
所以 C1「小程序资质法务确认」不是"以后再说",它直接决定学生能不能开始填报。
已知缺口:
· 填报主流程(/mp/login 门禁核验 表单填写)属 M3;
现在点「开始填报」给的是明确的"开发中"提示,不是静默无反应。
· frontend-mp 还没套项目既有的 ESLint/Stylelint 规范。
验收:e2e-m1-scene.py 31/31;八条 E2E 合计 389 项断言全通过;
微信开发者工具里人工确认页面渲染与 scene 传递正确。
|
2026-09-29 10:29:14 +08:00 |
|