From 748c4563fdb88b82639ba86b3e0303bbe1cd4ab6 Mon Sep 17 00:00:00 2001 From: jacobxu666 Date: Tue, 29 Sep 2026 16:58:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=AD=E5=A5=96=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E5=85=AC=E7=A4=BA=E9=A1=B5=E6=90=9C=E7=B4=A2=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E2=80=94=E2=80=94=E8=BE=93=E5=85=A5=E5=85=B3=E9=94=AE=E5=AD=97?= =?UTF-8?q?=E6=97=B6=E8=87=AA=E5=8A=A8=E6=8C=89=E5=A4=A7=E9=A1=B5=E6=8B=89?= =?UTF-8?q?=E5=85=A8=E5=BD=93=E6=9C=9F=E5=90=8D=E5=8D=95=E5=86=8D=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E8=BF=87=E6=BB=A4,=E4=BF=AE=E5=A4=8D=E6=9C=AA?= =?UTF-8?q?=E7=BF=BB=E9=A1=B5=E7=9A=84=E4=B8=AD=E5=A5=96=E8=80=85(?= =?UTF-8?q?=E5=A6=82=E7=8E=8B=E7=90=83=E8=8E=B2)=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=97=A0=E7=BB=93=E6=9E=9C=E7=9A=84=E9=97=AE=E9=A2=98;?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=97=A0=E5=A7=93=E5=90=8D=E5=8F=82=E6=95=B0?= =?UTF-8?q?,pageSize=3D500=C3=97N=20=E5=BE=AA=E7=8E=AF=E5=AE=9E=E6=B5=8B?= =?UTF-8?q?=204=20=E8=AF=B7=E6=B1=82=202000=20=E6=9D=A1,=E6=8B=89=E5=85=A8?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E9=9D=99=E9=BB=98=E9=99=8D=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- docs/2026-09-29-第一期中奖名单导入记录.md | 52 ++++++++++++++++++++++ pages/winners/winners.vue | 54 ++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 docs/2026-09-29-第一期中奖名单导入记录.md diff --git a/docs/2026-09-29-第一期中奖名单导入记录.md b/docs/2026-09-29-第一期中奖名单导入记录.md new file mode 100644 index 0000000..c111c5d --- /dev/null +++ b/docs/2026-09-29-第一期中奖名单导入记录.md @@ -0,0 +1,52 @@ +# 第一期中奖名单导入记录(2026-09-29) + +## 背景 + +怀化市社保卡千万好礼促消费活动第一期开奖名单产生后,需导入 UAT 库 `dscp_business_uat.hhrs_winner` 表,供公示页 https://hh.hn12301.net/#/pages/winners/winners 展示。 + +## 数据来源 + +| 文件 | 内容 | +| --- | --- | +| `抽奖-中奖数据.xlsx`(2026-09-29 10:21 导出) | 第一期中奖名单原始文件:2000 人(姓名+脱敏身份证号) | +| `抽奖-中奖数据-匹配hhrs_lottery_pool-batch1.csv` | 与抽奖池 `hhrs_lottery_pool`(batch_no=1)匹配结果:2001 行(1 人双卡) | + +## 导入内容与校验 + +- 导入通道:直连 UAT MySQL,事务内导入,前后计数复核通过。 +- 导入行数:**2000**(batch1 CSV 2001 行按 姓名+证件号 去重;会同县「杨明 431225********4219」池中有两条卡记录,保留 pool_id 较小的一条,业务侧如需明确以哪张卡中奖需另行核实)。 +- 表结构:UAT 库 `hhrs_winner` 实际为抽奖池版结构(`id, pool_id, member_name, county, member_identity_no, member_phone, created_time, updated_time`),无奖品/开奖日期列。 +- 字段取值:`id` 与 `pool_id` 均取抽奖池记录 id(一一对应、可溯源);`county` 取自池表;证件/手机号为池表脱敏值(库内即脱敏,公示展示不受影响);`created_time=NOW()`(2026-09-29 14:27)。 +- 同步操作:对应 2000 条池记录 `won` 置 1(与抽奖台「公布即回写」语义一致)。 +- 旧数据:导入前表为空,无清理动作。 + +## 已知问题(导入前即存在) + +dscp-uat 当前部署的后端与上表结构不匹配: + +- `mp-api/hhrsWinner/dates`、`mp-api/hhrsWinner/list` 均返回 `errcode 99999`(部署代码按旧 prize 版 schema 查询,`draw_date/card_model_id` 列已不存在); +- `open-api/hhrsDraw/*`(抽奖台后台)在该服务上为 404,抽奖台实际使用的后端未部署在此环境。 + +因此公示页当前显示「暂无数据/接口失败」,**与本次导入的数据无关**;待与现表结构匹配的新版后端部署后,第一期名单即可展示,数据无需重导。 + +## 补充排查发现(2026-09-29 下午) + +1. UAT 库中还存在抽奖后台配套表 `hhrs_draw_activity` / `hhrs_draw_round`(2026-09-28 建),即抽奖台新版后端的排期数据: + - **第一期官方开奖日期 = 2026-09-29**(「2026 金秋千万好礼 · 第一期(9·29 专场)」,活动 status=1 已发布); + - 排期设计:9·29 专场三场 600+700+700=2000 人,分别绑定 batch 1/2/3;全部轮次 status=0(待开奖)、seed/drawn_time 为空 → **抽奖台自动回写链路从未成功跑通**(与 dscp-uat 上 hhrsDraw 接口 404 相符); + - 实际交付的第一期名单是「batch1 一次抽 2000 人」,与排期表的三场拆分不一致——若后续场次仍按排期表取 batch2/3,不受本次导入影响(批次天然互斥)。 +2. 公示页要立即显示名单,零部署方案为:`hhrs_winner` 增加旧接口所需 8 列(card_model_id/member_id/prize_*/draw_date/remark,均可空,兼容新版写入),并回填:card_model_id=747609734826757、draw_date='2026-09-29'、prize_name='怀化市社保卡千万好礼促消费千元权益包'、prize_type='权益包'、prize_amount=100000(分,即 1000 元)。**待用户批准后执行**。 +3. 前端「隐藏手机号」改动(本地已完成)生效路径:HBuilderX CLI 可本地构建,产物上传 121.43.239.220:/home/hh/html 需要 SSH 权限(凭据在 Obsidian 运维手册)。 + +## 前端配套改动(本地已改,未发布) + +`hhrs/pages/winners/winners.vue`: + +1. 按业务要求移除「手机号码」列(表头、行、样式、按手机号过滤逻辑),搜索框改为仅按姓名。 +2. 修复「名单未翻到的人物搜索无结果」(如 王球莲):搜索原本只在前端已加载的 10 条/页内过滤,后端列表接口无姓名检索参数;现改为输入关键字/点搜索时按 500 条大页自动拉全当期名单(实测第一期 4 次请求共 2000 条)再本地过滤,全量结果按期缓存;拉取失败静默降级为已加载部分可搜。 + +需 HBuilderX 构建后上传 121.43.239.220:/home/hh/html 生效(`sh h5-release.sh` → 产物 scp 原子切换)。 + +## 发布结果(2026-09-29 16:07 线上实测) + +已发布生效:入口 `index.8ec4f995.js` → `index.e438708b.js`,修复分片 `pages-winners-winners.a875331e.js`(含 loadAll 全量搜索)公网 200。浏览器实测:不翻页直接搜「王球莲」命中 1 条(4 个 pageSize=500 请求自动拉全),姓名/身份证/手机号三列正常。四站点回归 hh/pxdy/mkh5/api/dw 全 200。发布过程偏差(目录切换替代覆盖式)已按手册补救:旧 hash 资源全量还原、`._*` AppleDouble 清理、webroot 备份 `/root/hh-html-backup-20260929-presearchfix.tar.gz`。winners.vue 源码改动尚未 commit。 diff --git a/pages/winners/winners.vue b/pages/winners/winners.vue index f12786d..d1095af 100644 --- a/pages/winners/winners.vue +++ b/pages/winners/winners.vue @@ -81,7 +81,11 @@ export default { // xuchao: 页码改为按期维护(原单一 pageNo 在切期后会被沿用,导致同一页重复追加/跳页) pageNos: {}, pageSize: 10, + // xuchao: 搜索触发的全量拉取用大页尺寸(列表接口仅支持分页参数,无姓名检索) + pageSizeFull: 500, totals: {}, + fullLoaded: {}, + loadingAll: {}, totalWinners: 0, inFlight: 0, loadStatusMap: {}, @@ -104,6 +108,15 @@ export default { return this.loadStatusMap[this.currentTab] || 'nomore' } }, + watch: { + // xuchao: 输入关键字即后台拉全当期名单,修复"没翻到的人物搜索无结果" + keyword(val) { + clearTimeout(this._kwTimer) + if (val && val.trim() && !this.fullLoaded[this.currentTab]) { + this._kwTimer = setTimeout(() => this.loadAll(this.currentTab), 400) + } + } + }, onLoad() { this.loadDates() }, @@ -155,6 +168,8 @@ export default { pageSize: this.pageSize, drawDate: drawDate }).then(res => { + // xuchao: 全量拉取已就绪/进行中时,旧的10条分页响应直接丢弃,避免覆盖全量列表 + if (this.fullLoaded[tabIndex] || this.loadingAll[tabIndex]) return if (res.errcode === 0 && res.data) { const list = res.data.list || [] const prev = this.winnerData[tabIndex] || [] @@ -177,12 +192,48 @@ export default { }, loadMore() { const tab = this.currentTab + if (this.loadingAll[tab]) return const total = this.totals[tab] || 0 const len = (this.winnerData[tab] || []).length if (len < total && this.inFlight === 0) { this.loadWinnerList(tab, (this.pageNos[tab] || 1) + 1) } }, + // xuchao: 按大页把当期名单一次拉全,搜索即可命中未翻到的人;完成后整期替换并标记 fullLoaded + loadAll(tabIndex) { + const tab = this.tabs[tabIndex] + if (!tab || this.fullLoaded[tabIndex] || this.loadingAll[tabIndex]) return + this.$set(this.loadingAll, tabIndex, true) + this.$set(this.loadStatusMap, tabIndex, 'loading') + const acc = [] + const pull = (pageNo) => this.$u.api.getWinnerList({ + pageNo: pageNo, + pageSize: this.pageSizeFull, + drawDate: tab.date + }).then(res => { + if (res.errcode !== 0 || !res.data) return + const list = res.data.list || [] + const total = res.data.total || 0 + acc.push(...list) + this.$set(this.totals, tabIndex, total) + if (list.length > 0 && acc.length < total && pageNo < 50) return pull(pageNo + 1) + }) + pull(1).then(() => { + if (acc.length > 0) { + this.$set(this.winnerData, tabIndex, acc) + this.$set(this.fullLoaded, tabIndex, true) + this.$set(this.pageNos, tabIndex, Math.ceil(acc.length / this.pageSizeFull)) + } + this.totalWinners = Object.values(this.totals).reduce((s, n) => s + n, 0) + }).catch(() => { + // 拉全失败静默降级为已加载部分可搜 + }).finally(() => { + this.$set(this.loadingAll, tabIndex, false) + const len = (this.winnerData[tabIndex] || []).length + const total = this.totals[tabIndex] || 0 + this.$set(this.loadStatusMap, tabIndex, len === 0 || len >= total ? 'nomore' : 'loadmore') + }) + }, switchTab(index) { this.currentTab = index // xuchao: 不再重置全局 pageNo(各期游标自带);空列表补拉首页时允许并发, @@ -204,7 +255,8 @@ export default { } }, onSearch() { - // 前端过滤,无需重新请求 + // xuchao: 点搜索立即确保当期全量已拉取(过滤仍在前端本地完成) + if (!this.fullLoaded[this.currentTab]) this.loadAll(this.currentTab) } } }