From ca0db57f24caf3cf3115aa1b5f14eca625292f58 Mon Sep 17 00:00:00 2001 From: WorkBuddy Date: Wed, 30 Sep 2026 12:10:00 +0800 Subject: [PATCH] =?UTF-8?q?chore(release):=20=E5=86=BB=E7=BB=93=20v1.1.0?= =?UTF-8?q?=20=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 同步版本号:VERSION / backend pom / admin-web / miniapp 均升至 1.1.0 - 合并本轮代码审查修复(详见 docs/CODE-REVIEW-2026-09-24.md): - 后端:微信登录真实实现 RealWechatLoginProvider、实名核验真实实现 RealIdentityVerificationProvider(本地强校验 + 可配置第三方 + fail-closed/fail-fast) - 照片直传落库端点 POST /me/photos/confirm(fileKey 归属校验 + 对象存在性校验后 savePhoto) - 刷新 Token Redis TTL 修正(getExpirationMs()*2 的 4h → refreshExpirationMs 的 14 天) - 管理端 token 改为 HttpOnly Cookie + silent-refresh,移除 localStorage 明文存储与 30+ 处手工注入 - 小程序:红娘接单鉴权头错位修复、serviceType 中文映射、生产域名可配置 - 隔离本地产物(.workbuddy/、*.bundle、*.dump、secrets、application-dev.yml 等已被 .gitignore 覆盖) 未验证项(打标签 ≠ 生产发布): - 后端编译 / 单测未在本环境执行(Maven 慢、Docker DNS 受限) - 管理端 登录→刷新→登出 端到端、小程序真机走查待人工验收 - 生产部署需注入 WECHAT_APP_ID / WECHAT_APP_SECRET、VERIFICATION_API_URL / VERIFICATION_API_KEY 与小程序生产域名 --- .github/CODEOWNERS | 37 ++++ .github/PULL_REQUEST_TEMPLATE.md | 69 ++++++ VERSION | 2 +- admin-web/package.json | 2 +- admin-web/pnpm-workspace.yaml | 3 + admin-web/src/api/client.ts | 48 ++++- .../modules/activity/ActivityCheckinView.vue | 3 +- .../modules/activity/ActivityInterestView.vue | 5 +- .../activity/ActivityManagementView.vue | 3 +- .../src/modules/analytics/AnalyticsView.vue | 5 +- .../src/modules/appeal/AppealCenterView.vue | 9 +- admin-web/src/modules/audit/AuditLogView.vue | 3 +- .../operator/OperatorManagementView.vue | 11 +- .../modules/organization/OrganizationView.vue | 7 +- .../src/modules/risk/ReportCenterView.vue | 9 +- admin-web/src/router/index.ts | 5 +- admin-web/src/stores/auth.ts | 73 +++++-- admin-web/src/utils/token.ts | 19 ++ .../miniapp/capsule/safety-third.png | Bin 0 -> 90745 bytes .../ui-redesign/miniapp/capsule/svc-third.png | Bin 0 -> 174815 bytes .../miniapp/capsule/top-activity.png | Bin 0 -> 194101 bytes .../miniapp/capsule/top-discover.png | Bin 0 -> 192688 bytes .../ui-redesign/miniapp/capsule/top-index.png | Bin 0 -> 478590 bytes .../miniapp/capsule/top-messages.png | Bin 0 -> 188532 bytes .../ui-redesign/miniapp/capsule/top-mine.png | Bin 0 -> 301994 bytes .../miniapp/home-entries-grid-zoom.png | Bin 0 -> 171651 bytes .../ui-redesign/miniapp/home-entries-page.png | Bin 0 -> 191779 bytes backend/pom.xml | 4 +- .../controller/OperatorAuthController.java | 80 ++++++- .../auth/cookie/OperatorCookieWriter.java | 50 +++++ .../provider/MockWechatLoginProvider.java | 9 +- .../provider/RealWechatLoginProvider.java | 100 +++++++++ .../platform/auth/service/AuthService.java | 8 +- .../auth/service/OperatorAuthService.java | 11 + .../platform/auth/token/JwtTokenService.java | 14 +- .../platform/me/controller/MeController.java | 19 ++ .../platform/photo/service/PhotoService.java | 25 +++ .../platform/shared/filter/JwtAuthFilter.java | 21 +- .../MockIdentityVerificationProvider.java | 9 +- .../RealIdentityVerificationProvider.java | 190 +++++++++++++++++ .../src/main/resources/application-docker.yml | 15 +- .../main/resources/application-production.yml | 24 +++ docs/CODE-REVIEW-2026-09-24.md | 178 ++++++++++++++++ docs/governance/CODE_REVIEW_STANDARDS.md | 196 ++++++++++++++++++ miniapp/constants/profileLabels.js | 16 ++ miniapp/env.js | 5 +- miniapp/env.ts | 10 +- miniapp/package-lock.json | 4 +- miniapp/package.json | 2 +- .../matchmaker-workbench.js | 10 +- .../matchmaker-workbench.wxml | 2 +- .../pages/profile-photos/profile-photos.js | 11 +- miniapp/services/profileService.js | 13 ++ 53 files changed, 1256 insertions(+), 83 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 admin-web/src/utils/token.ts create mode 100644 artifacts/ui-redesign/miniapp/capsule/safety-third.png create mode 100644 artifacts/ui-redesign/miniapp/capsule/svc-third.png create mode 100644 artifacts/ui-redesign/miniapp/capsule/top-activity.png create mode 100644 artifacts/ui-redesign/miniapp/capsule/top-discover.png create mode 100644 artifacts/ui-redesign/miniapp/capsule/top-index.png create mode 100644 artifacts/ui-redesign/miniapp/capsule/top-messages.png create mode 100644 artifacts/ui-redesign/miniapp/capsule/top-mine.png create mode 100644 artifacts/ui-redesign/miniapp/home-entries-grid-zoom.png create mode 100644 artifacts/ui-redesign/miniapp/home-entries-page.png create mode 100644 backend/src/main/java/com/danyang/match/platform/auth/cookie/OperatorCookieWriter.java create mode 100644 backend/src/main/java/com/danyang/match/platform/auth/provider/RealWechatLoginProvider.java create mode 100644 backend/src/main/java/com/danyang/match/platform/verification/provider/RealIdentityVerificationProvider.java create mode 100644 docs/CODE-REVIEW-2026-09-24.md create mode 100644 docs/governance/CODE_REVIEW_STANDARDS.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..2332a7c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,37 @@ +# CODEOWNERS — 善缘丹阳公益婚恋平台 审查角色分配(草案) +# +# 作用:开 PR 时 GitHub 自动向对应路径的 owner 请求审查;配合分支保护与 +# docs/governance/CODE_REVIEW_STANDARDS.md 的 T0/T1/T2 分级与双审规则使用。 +# 语法:https://docs.github.com/en/repositories/managing-your-repositories-settings-and-features/customizing-your-repository/about-code-owners +# +# 当前为一人团队,默认全部归技术负责人 @g1307092382。 +# 待扩员时,把各端拆给对应熟悉者即可;T2 高危改动按审查标准须 2 人复核(其中 1 人为该端 owner)。 +# 说明:CODEOWNERS 只负责「请求谁审」,不强制「必须不同人」;双审由审查流程纪律保证。 +# 排序:子路径必须放在父路径之后(GitHub 取最后一个匹配的规则)。 + +# —— 默认兜底 —— +* @g1307092382 + +# —— 后端(Spring Boot / Java)—— +/backend/ @g1307092382 + +# 数据库迁移(Flyway):schema 变更属 T2,须重点复核(放在 /backend/ 之后) +/backend/src/main/resources/db/migration/ @g1307092382 + +# —— 微信小程序(原生 WXML/WXSS/TS)—— +/miniapp/ @g1307092382 + +# —— 管理端(Vue3 / TS / pnpm)—— +/admin-web/ @g1307092382 + +# —— 测试与契约守卫(跨端契约,需熟悉各端)—— +/tests/ @g1307092382 +/ui-tests/ @g1307092382 +/scripts/run-ui-contracts.mjs @g1307092382 + +# —— 基础设施 / 部署(Compose / 镜像 / 密钥)—— +/infrastructure/ @g1307092382 + +# —— 治理与审查机制文档(变更须经技术负责人确认)—— +/docs/governance/ @g1307092382 +/.github/ @g1307092382 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..dcb486b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,69 @@ + + +## 变更摘要 + + +## 风险分级(见审查标准 §2) +- [ ] T0 琐碎(纯文案/文档/格式化/资源重命名) +- [ ] T1 常规(单端功能/Bug/UI/单 API) +- [ ] T2 高危(鉴权/支付/隐私/跨端契约/DB schema/并发幂等/已知阻塞项)— 需 2 人复核 + +## 验证方式(必填,证据优先) + +- 本地测试/契约守卫命令与结果: +- 运行时实测(如涉及渲染/接口,贴 mock/路由/关键响应): +- 负向注入(新增或修改测试时必填:故意改坏后测试是否转红): + +## 技术栈审查清单(按本 PR 涉及项打勾) +### 后端 Java / Spring Boot +- [ ] 业务拒绝用 `BusinessException`+`ApiResponseCode`(HTTP 400≤code<500),无裸 `RuntimeException` +- [ ] 测试对业务拒绝断言 `400 ≤ status < 500`(非仅 `!= 200`) +- [ ] 核心不变量(INV-014/015、Required/Mutual/Block 等)未被绕过 +- [ ] 主键/唯一键/CHECK 约束与 Java 枚举一致,无静默 no-op 写 +- [ ] 无新增死表/死配置(`system_configs`/`match_rule_configs` 类) + +### 小程序 miniapp +- [ ] `app.json`/`pages.json` 路径与磁盘文件一致、无前导 `/` +- [ ] `ignoreDevUnusedFiles`/`ignoreUploadUnusedFiles` = false(资源不被吞) +- [ ] 跨端字段名与后端规范名一致(tsc 不保护 JS 层字段名) +- [ ] WXSS 无通配符/属性选择器/嵌套注释;`` 显式高度 +- [ ] 胶囊安全区让出 `--nav-right` + +### 管理端 admin-web +- [ ] 渲染相关状态走 `ref`/`reactive`,已补渲染态实测 +- [ ] 颜色/字号收敛到 `src/theme/` token,EP 主色阶补全 `-light-N`/`-dark-2`/`-rgb` +- [ ] 表单校验类型安全、出错保留输入 +- [ ] 触摸目标 ≥44px,尊重 `prefers-reduced-motion` + +### 数据库迁移 SQL +- [ ] `V<序号>__<描述>.sql` 命名,版本连续唯一 +- [ ] 向后兼容,NOT NULL 新列有默认值/迁移 +- [ ] CHECK 约束与枚举一致,破坏性变更有回滚方案 + +### 测试 / 契约守卫 +- [ ] 无空壳 `skip`/`pass` 测试 +- [ ] 已做负向注入证明判别力 +- [ ] `scripts/run-ui-contracts.mjs` 的 `MIN_GUARDS`(当前 23) 同步(增删守卫必改) + +### 脚本 / 基础设施 +- [ ] 脚本真实依赖已在 CI 对应 job 安装(非仅本地能跑) +- [ ] 密钥仅运行期、不落盘;本地栈固定 `-p danyang` + +## 判据纪律自查(见审查标准 §5,对应改动必查) +- [ ] 非空数据验证(无空数据假绿,如推荐/列表返回空也能过) +- [ ] 无直连绕过(关键路径至少一个经 HTTP 层断言) +- [ ] 约束相关改动在带约束环境验证(无测试库缺约束假绿) +- [ ] 小程序改动不走 `Mini Program Compile Check` 的"仅 tsc"假绿,已真编译/查资源 +- [ ] 合并后回读 `git rev-parse HEAD` + 轮询 CI 确认本次 head 真 success + +## 已知阻塞 / 决策关联 +- [ ] 本 PR 是否触碰上线阻塞项(微信真实登录 / 照片直传落库)?若是:✅本次解决 / ⚠️未解决已登记 known-risks +- [ ] 是否与 `decision-register` 的 LOCKED 决策冲突?若是:已回决策层确认(附结论) +- [ ] 是否产生新债务?若是:已登记 `known-risks.md`(编号 KR-xx) + +## 关联 + diff --git a/VERSION b/VERSION index 6e28fd6..9084fa2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0-RC.1 +1.1.0 diff --git a/admin-web/package.json b/admin-web/package.json index d0120e8..a93468e 100644 --- a/admin-web/package.json +++ b/admin-web/package.json @@ -1,6 +1,6 @@ { "name": "danyang-match-admin", - "version": "1.0.0-rc.2", + "version": "1.1.0", "private": true, "type": "module", "scripts": { diff --git a/admin-web/pnpm-workspace.yaml b/admin-web/pnpm-workspace.yaml index 2a7b9ba..4cc8c79 100644 --- a/admin-web/pnpm-workspace.yaml +++ b/admin-web/pnpm-workspace.yaml @@ -19,6 +19,9 @@ # 后者生成 Vue 版本桥接),允许它们执行。 packages: - '.' +allowBuilds: + esbuild: set this to true or false + vue-demi: set this to true or false onlyBuiltDependencies: - esbuild - vue-demi diff --git a/admin-web/src/api/client.ts b/admin-web/src/api/client.ts index a8893bd..1eb6889 100644 --- a/admin-web/src/api/client.ts +++ b/admin-web/src/api/client.ts @@ -1,14 +1,18 @@ import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' +import { getAccessToken, setAccessToken, clearAccessToken } from '@/utils/token' const client = axios.create({ baseURL: '/api/v1', timeout: 10000, }) +// 防止 401 时并发触发多次刷新 +let refreshing = false + client.interceptors.request.use((config) => { - const token = localStorage.getItem('accessToken') + const token = getAccessToken() if (token) { config.headers.Authorization = `Bearer ${token}` } @@ -17,11 +21,45 @@ client.interceptors.request.use((config) => { client.interceptors.response.use( (res) => res.data, - (err) => { - if (err.response?.status === 401) { - localStorage.removeItem('accessToken') - router.push('/login') + async (err) => { + const status = err.response?.status + const original = err.config + const isAuthEndpoint = !!original?.url?.includes('/auth/operator/') + + // access token 过期:尝试用 HttpOnly refresh Cookie 静默刷新一次后重试原请求 + // __retried 防止刷新后重试仍 401 时再次触发刷新,形成循环 + if (status === 401 && !refreshing && !isAuthEndpoint && !(original as any)?.__retried) { + refreshing = true + try { + const resp: any = await client.post('/auth/operator/refresh', {}) + if (resp?.success) { + const newToken = resp.data.accessToken + setAccessToken(newToken) + refreshing = false + ;(original as any).__retried = true + const headers = (original.headers || {}) as Record + headers.Authorization = `Bearer ${newToken}` + original.headers = headers as any + return client(original) + } + } catch { + // 刷新失败:清除并跳登录 + } finally { + refreshing = false + } + clearAccessToken() ElMessage.error('登录已过期,请重新登录') + router.push('/login') + return Promise.reject(err) + } + + // 刷新/登录等鉴权端点自身 401:交由调用方处理文案,不全局弹窗跳转 + if (status === 401) { + if (!isAuthEndpoint) { + clearAccessToken() + ElMessage.error('登录已过期,请重新登录') + router.push('/login') + } } else if (err.response?.data?.message) { ElMessage.error(err.response.data.message) } else { diff --git a/admin-web/src/modules/activity/ActivityCheckinView.vue b/admin-web/src/modules/activity/ActivityCheckinView.vue index defcf01..873cd95 100644 --- a/admin-web/src/modules/activity/ActivityCheckinView.vue +++ b/admin-web/src/modules/activity/ActivityCheckinView.vue @@ -80,6 +80,7 @@