feat(pickup): 店铺管理支持历史店铺可视化手动映射与一键批量清洗

- 数据库升级:新增 pickup_shop_mappings 映射别名表,并在 admin_pickups 增加 raw_shop_name 永久保留原始店铺录入值
- 后端映射能力:提供历史店铺聚合统计、单条/批量映射至目标标准店铺、映射规则撤销与提号单还原接口;新建提号时若录入已映射别名自动归一化
- 前端管理升级:PickupShopDialog 升级为双 Tab 结构,Tab 2「历史店铺映射与清洗」支持总览统计卡片、状态筛选、单条/批量映射清洗、一键设为标准店铺及撤销还原
- 验证方式:执行并通过全套代码门禁(scripts/check.sh:SQL约束、go vet/build/test、MySQL测试库覆盖率、vue大小、eslint、prettier、vue-tsc及vite构建)
This commit is contained in:
yml2213
2026-10-09 13:31:11 +08:00
parent 9d9adfbd5e
commit f3c5235b81
12 changed files with 1187 additions and 100 deletions
@@ -383,6 +383,55 @@ func (h *Handler) DeleteShop(c *gin.Context) {
response.OK(c, gin.H{"deleted": true})
}
// GetHistoricalShopStats 获取历史店铺聚合统计及映射情况(管理员)
func (h *Handler) GetHistoricalShopStats(c *gin.Context) {
stats, err := h.service.GetHistoricalShopStats(c.Request.Context())
if err != nil {
writePickupError(c, err)
return
}
response.OK(c, stats)
}
// MapHistoricalShops 批量映射历史店铺并清洗提号单(管理员)
func (h *Handler) MapHistoricalShops(c *gin.Context) {
adminID, ok := currentAdminID(c)
if !ok {
response.Unauthorized(c, "缺少管理员上下文")
return
}
var req MapHistoricalShopsRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "映射参数格式不正确")
return
}
res, err := h.service.MapHistoricalShops(c.Request.Context(), req, adminID, auditMeta(c))
if err != nil {
writePickupError(c, err)
return
}
response.OK(c, res)
}
// DeleteShopMapping 删除历史店铺映射规则并还原提号单(管理员)
func (h *Handler) DeleteShopMapping(c *gin.Context) {
adminID, ok := currentAdminID(c)
if !ok {
response.Unauthorized(c, "缺少管理员上下文")
return
}
sourceName := c.Query("source_name")
if sourceName == "" {
response.BadRequest(c, "缺少 source_name 参数")
return
}
if err := h.service.DeleteShopMapping(c.Request.Context(), sourceName, adminID, auditMeta(c)); err != nil {
writePickupError(c, err)
return
}
response.OK(c, gin.H{"deleted": true})
}
func auditMeta(c *gin.Context) auditlog.Meta {
return auditlog.Meta{
IP: c.ClientIP(),