Compare commits

..
7 Commits
Author SHA1 Message Date
jacobxu666andClaude Fable 5 9276bd6140 合并:中奖名单公示页搜索修复(feature/venue-discount-rights-filter → xuchao)
冲突解决:venue 列表灰底间距与卡片内边距取 feature 侧设计修正;card-address 保留 min-width:0;index 页面取页级滚动方案。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-29 17:02:29 +08:00
jacobxu666andClaude Fable 5 748c4563fd fix: 中奖名单公示页搜索修复——输入关键字时自动按大页拉全当期名单再本地过滤,修复未翻页的中奖者(如王球莲)搜索无结果的问题;接口无姓名参数,pageSize=500×N 循环实测 4 请求 2000 条,拉全失败静默降级
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-29 16:58:42 +08:00
jacobxu666andClaude Fable 5 2a6b86977c feat: 商铺详情隐藏汽油券购买入口(业务调整:该商铺不再特殊处理购买,券页面/接口/SDK注入均保留)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-28 22:22:50 +08:00
jacobxu666andClaude Fable 5 044b26d035 feat: 控件唤起失败提示改为「支付已取消,请重新支付」,[诊断]行暂留采集真实错误码以便区分取消与容器不支持
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-28 20:02:44 +08:00
jacobxu666 381e609001 Merge branch 'master' into xuchao 2026-09-03 16:29:20 +08:00
jacobxu666 41a380562a 添加.gitignore,忽略构建产物和node_modules 2026-07-17 11:09:59 +08:00
jacobxu666 476cb64ee2 优化首页、场馆、折扣页面卡片布局及宽屏适配
- 首页:banner限制最大高度300px,页面支持滚动
- 场馆/折扣:卡片结构统一,地址和距离合并容器靠右对齐
- 卡片宽度自适应屏幕,间距统一margin 12px
- card-name填充容器,超长截断
2026-07-17 11:08:57 +08:00
7 changed files with 146 additions and 13 deletions
+1
View File
@@ -7,6 +7,7 @@ export const VOUCHER_PRICE_TEXT = '200.00'
export const VOUCHER_NAME = '怀化石油汽油电子券' export const VOUCHER_NAME = '怀化石油汽油电子券'
export const VOUCHER_GUIDE_TEXT = '请在「云闪付 App」里的「湖南社保卡」小程序首页进入「怀化社保卡」活动中完成购买' export const VOUCHER_GUIDE_TEXT = '请在「云闪付 App」里的「湖南社保卡」小程序首页进入「怀化社保卡」活动中完成购买'
export const VOUCHER_CANCEL_TEXT = '支付已取消,请重新支付'
// 券详情页「使用规则 / 使用流程 / 重要须知」文案,来自石油侧材料(决策 14) // 券详情页「使用规则 / 使用流程 / 重要须知」文案,来自石油侧材料(决策 14)
export const VOUCHER_USE_RULES = [ export const VOUCHER_USE_RULES = [
@@ -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。
+6
View File
@@ -185,6 +185,12 @@
flex-direction: column; flex-direction: column;
} }
.banner {
width: 100%;
max-height: 300px;
overflow: hidden;
}
.banner-img { .banner-img {
width: 100%; width: 100%;
display: block; display: block;
+7 -4
View File
@@ -52,7 +52,7 @@
<script> <script>
import { import {
VOUCHER_NAME, VOUCHER_PRICE_TEXT, VOUCHER_GUIDE_TEXT, VOUCHER_NAME, VOUCHER_PRICE_TEXT, VOUCHER_GUIDE_TEXT, VOUCHER_CANCEL_TEXT,
bizOk, bizMsg, bizData bizOk, bizMsg, bizData
} from '@/common/petrolVoucher.js' } from '@/common/petrolVoucher.js'
@@ -165,6 +165,8 @@ export default {
const ready = await this.loadUpSdk() const ready = await this.loadUpSdk()
if (!ready) { if (!ready) {
this.submitting = false this.submitting = false
// xuchao: 临时诊断(定位弹窗真因后移除):SDK 加载不出来时展示 UA 片段
this.guideText = '支付SDK未能加载[诊断] UA=' + (typeof navigator !== 'undefined' ? String(navigator.userAgent).slice(0, 120) : 'N/A')
this.guideModal = true this.guideModal = true
return return
} }
@@ -174,10 +176,11 @@ export default {
success: () => { success: () => {
uni.redirectTo({ url: '/pages/petrol/voucherDetail?orderNo=' + this.orderNo }) uni.redirectTo({ url: '/pages/petrol/voucherDetail?orderNo=' + this.orderNo })
}, },
fail: () => { fail: (err) => {
// xuchao: 唤起失败(多为不在云闪付内)→ 指引模态;云闪付内取消也会走这里, // xuchao: 控件回调失败(含用户取消)。按 xuchao 要求文案定为取消提示;
// 订单仍保留可"继续支付",统一用文案引导,不做环境猜测 // [诊断] 行临时保留采集真实错误码,区分"取消"与"容器不支持"后移除并分文案
this.submitting = false this.submitting = false
this.guideText = VOUCHER_CANCEL_TEXT + '\n[诊断] ' + JSON.stringify(err || {})
this.guideModal = true this.guideModal = true
} }
}) })
+3 -1
View File
@@ -113,8 +113,10 @@ export default {
return this.detailType !== '2' return this.detailType !== '2'
}, },
isPetrol() { isPetrol() {
// xuchao: 业务调整:该商铺不再需要特殊购买处理,前端入口整体隐藏;券页面/接口/SDK注入均保留,恢复时去掉下面的 return false 即可
return false
// xuchao: 后端券服务探活(latest 返回 price)后才展示购券模块——后端未部署期间整模块自动隐藏,无死入口 // xuchao: 后端券服务探活(latest 返回 price)后才展示购券模块——后端未部署期间整模块自动隐藏,无死入口
return this.loaded && String(this.shopId) === PETROL_SHOP_ID && !this.isVenue && !!(this.voucher && this.voucher.price) // return this.loaded && String(this.shopId) === PETROL_SHOP_ID && !this.isVenue && !!(this.voucher && this.voucher.price)
}, },
voucherBtnText() { voucherBtnText() {
const s = this.voucher ? this.voucher.statusCode : -1 const s = this.voucher ? this.voucher.statusCode : -1
+24 -7
View File
@@ -29,11 +29,13 @@
<view class="card-info"> <view class="card-info">
<text class="card-name">{{ item.name }}</text> <text class="card-name">{{ item.name }}</text>
<text class="card-category">{{ item.category }}</text> <text class="card-category">{{ item.category }}</text>
<text class="card-address">{{ item.address }}</text> <view class="card-bottom">
</view> <text class="card-address">{{ item.address }}</text>
<view class="card-distance" v-if="item.distance !== null"> <view class="card-distance" v-if="item.distance !== null">
<u-icon name="map" color="#999999" size="24"></u-icon> <u-icon name="map" color="#999999" size="24"></u-icon>
<text class="distance-text">{{ item.distance < 1 ? Math.round(item.distance * 1000) + 'm' : Math.round(item.distance) + 'km' }}</text> <text class="distance-text">{{ item.distance < 1 ? Math.round(item.distance * 1000) + 'm' : Math.round(item.distance) + 'km' }}</text>
</view>
</view>
</view> </view>
</view> </view>
<view v-if="hasScrolled" class="nomore-area"> <view v-if="hasScrolled" class="nomore-area">
@@ -239,7 +241,7 @@ export default {
padding: 24px 12px; padding: 24px 12px;
gap: 12px; gap: 12px;
box-shadow: 0 2px 3px #f2f2f7; box-shadow: 0 2px 3px #f2f2f7;
margin-bottom: 12px; margin: 12px;
} }
.card-thumb { .card-thumb {
@@ -282,12 +284,18 @@ export default {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0; gap: 0;
width: 100%;
min-width: 0;
} }
.card-name { .card-name {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: #000000; color: #000000;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.card-category { .card-category {
@@ -299,13 +307,22 @@ export default {
line-height: 20px; line-height: 20px;
} }
.card-bottom {
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 8px;
gap: 8px;
}
.card-address { .card-address {
flex: 1;
font-size: 14px; font-size: 14px;
color: #727272; color: #727272;
margin-top: 8px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-width: 0;
} }
.card-distance { .card-distance {
+53 -1
View File
@@ -81,7 +81,11 @@ export default {
// xuchao: 页码改为按期维护(原单一 pageNo 在切期后会被沿用,导致同一页重复追加/跳页) // xuchao: 页码改为按期维护(原单一 pageNo 在切期后会被沿用,导致同一页重复追加/跳页)
pageNos: {}, pageNos: {},
pageSize: 10, pageSize: 10,
// xuchao: 搜索触发的全量拉取用大页尺寸(列表接口仅支持分页参数,无姓名检索)
pageSizeFull: 500,
totals: {}, totals: {},
fullLoaded: {},
loadingAll: {},
totalWinners: 0, totalWinners: 0,
inFlight: 0, inFlight: 0,
loadStatusMap: {}, loadStatusMap: {},
@@ -104,6 +108,15 @@ export default {
return this.loadStatusMap[this.currentTab] || 'nomore' 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() { onLoad() {
this.loadDates() this.loadDates()
}, },
@@ -155,6 +168,8 @@ export default {
pageSize: this.pageSize, pageSize: this.pageSize,
drawDate: drawDate drawDate: drawDate
}).then(res => { }).then(res => {
// xuchao: 全量拉取已就绪/进行中时,旧的10条分页响应直接丢弃,避免覆盖全量列表
if (this.fullLoaded[tabIndex] || this.loadingAll[tabIndex]) return
if (res.errcode === 0 && res.data) { if (res.errcode === 0 && res.data) {
const list = res.data.list || [] const list = res.data.list || []
const prev = this.winnerData[tabIndex] || [] const prev = this.winnerData[tabIndex] || []
@@ -177,12 +192,48 @@ export default {
}, },
loadMore() { loadMore() {
const tab = this.currentTab const tab = this.currentTab
if (this.loadingAll[tab]) return
const total = this.totals[tab] || 0 const total = this.totals[tab] || 0
const len = (this.winnerData[tab] || []).length const len = (this.winnerData[tab] || []).length
if (len < total && this.inFlight === 0) { if (len < total && this.inFlight === 0) {
this.loadWinnerList(tab, (this.pageNos[tab] || 1) + 1) 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) { switchTab(index) {
this.currentTab = index this.currentTab = index
// xuchao: 不再重置全局 pageNo(各期游标自带);空列表补拉首页时允许并发, // xuchao: 不再重置全局 pageNo(各期游标自带);空列表补拉首页时允许并发,
@@ -204,7 +255,8 @@ export default {
} }
}, },
onSearch() { onSearch() {
// 前端过滤,无需重新请求 // xuchao: 点搜索立即确保当期全量已拉取(过滤仍在前端本地完成)
if (!this.fullLoaded[this.currentTab]) this.loadAll(this.currentTab)
} }
} }
} }