Files
guilixu-admin/.workbuddy/memory/MEMORY.md
T
gxwebsoft ced8c7cff7 feat(shop-zone): 新增专区销量统计功能
- 后端新增专区销量统计相关 VO 并扩展 ShopHomeSection 实体
- 新增批量查询专区销量汇总与商品排行的 Mapper 方法及接口
- Controller 增加获取专区销量统计的接口支持时间范围查询
- 前端接口定义新增专区销量统计类型及请求方法
- 专区管理页面列表新增销量件数、销售额列及销量详情按钮
- 实现专区销量统计弹窗支持时间筛选、汇总展示和排行显示
- 完成前端相关界面和交互设计,保证功能完整可用
- 统计口径基于已支付且未取消/退款订单商品实际成交数据
2026-08-17 12:40:08 +08:00

11 KiB
Raw Blame History

项目长期记忆

项目结构

关键项目路径

  • 管理后台/Users/gxwebsoft/VUE/guilixu-adminVue3 + Ant Design Vue
  • 用户端小程序/Users/gxwebsoft/VUE/guilixu-taroTaro + React
  • 商家端小程序/Users/gxwebsoft/VUE/xinlong-shop-taroTaro + React

⚠️ 跨项目教训(重要)

  • 商家端发货/门店/订单相关功能在 xinlong-shop-taro,不要改到 guilixu-taro
  • 用户端订单详情等功能在 guilixu-taro
  • 跨项目改动前,先用 grep/glob 在多个候选项目里确认"哪个项目有该功能源码",再动手
  • 已发生两次误改 guilixu-taro 的教训(useClerk 全局上下文、订单详情 deliveryType 修复)

业务约定

deliveryType 配送方式枚举

  • 0 = 快递配送
  • 1 = 无需发货 / 自提
  • 2 = 商家送货

来源:管理后台 src/views/shop/shopOrder/components/deliveryModal.vue

Java 后端架构(重要)

双后端 / MODULES_API_URL 模式(2026-07-25 分析结论)

  • mp-java/Users/gxwebsoft/JAVA/mp-java)= 独立的「modules 服务」,用 modules 库;cms(235类)、shop 等业务模块都在它这里,持续高频开发。
  • guilixu-java/Users/gxwebsoft/JAVA/guilixu-java= 桂礼序主后端,用 db_guilixu 库;是 mp-java 的「子集分叉」。
  • guilixu-admin 前端已预设双基地址SERVER_API_URL(主后端) + MODULES_API_URL(localStorage ApiUrl / VITE_API_URL)。
  • ⚠️ 运行时实际指向以 localStorage ApiUrl 为准2026-08-06 用户纠正):guilixu-admin 的「商城」接口实际调 https://shop-api.websoft.top= guilixu-java,并非 mp-java。判断"改哪个后端才生效"不能默认 MODULES_API_URL=mp-java,必须查 ApiUrl 运行时配置。 cms 接口另走 VITE_CMS_API_URL=cms-api.websoft.topmp-java)。
  • 结论(仅限 cms 模块):guilixu-java 里的 cms 包(152类)是 mp-java cms 的旧分叉子集、且前端并不调它(前端调 modules 服务),属重复/死代码,应删除而非复制或扩展。
  • 复制「整个 cms 到 guilixu-java」不可行:mp-java 完整 cms 的 CmsApp/CmsWebsiteService 依赖 project 模块(guilixu-java 没有),会编译失败。
  • 注意:mp-java 与 guilixu-java 是独立 git 仓库(git.websoft.top/gxwebsoft/mp-java.gitguilixu-java.git),各自独立部署、独立库。

⚠️ 双后端改动同步规则(2026-08-05 修正)

  • cms 模块改动:仅 mp-javaguilixu-java 的 cms 是死代码)。
  • shop 等共享业务模块(如 ShopGoodsController / Service / Mapper / task):mp-java 与 guilixu-java 都有活代码且结构一致(同 com.gxwebsoft.shop 包),用户确认改动要两端同步(例:2026-08-05 shopGoods /data 统计优化,两端各改 6 处)。
  • 改动前先 grep 两端确认文件是否存在,避免只改一处导致另一端行为不一致。

⚠️ 运费模板不发货地区需求(2026-08-06 结论,已按用户纠正修正)

  • 需求:运费模板支持「不发货地区(黑名单)」,ShopExpressTemplateDetail 加 regionMode/regionIds 字段 + DeliveryRegionChecker 下单校验。
  • 后端(guilixu-java)已完整实现entity/param/迁移SQL/DeliveryRegionChecker/RegionCodeResolver/OrderBusinessService 接入(第 87 行调用 validateDeliveryRegionIfNeeded)均就位。
  • 关键事实(用户 2026-08-06 18:09 纠正):guilixu-admin 的「商城」接口实际调 https://shop-api.websoft.top= guilixu-java。前端 shopExpressTemplateDetail 用默认 @/utils/requestbaseURL = MODULES_API_URL = localStorage ApiUrl || VITE_API_URL;本部署的 ApiUrl 即 shop-api.websoft.top/api。故 guilixu-java 是生效后端,其实现正确生效,不是死代码。
  • 结论:本功能(admin 配置)无需改 mp-java(本部署 商城不走 mp-java)。
  • 端到端链路已确认打通(2026-08-12):用户端 guilixu-taroBaseUrl = https://shop-api.websoft.top/apiconfig/env.js 全环境),TenantId='10606',下单 POST /shop/shop-order 即走 guilixu-java(db_guilixu);且单租户下买家 tenantId=10606(微信支付证书也依赖此值),故 DeliveryRegionCheckertenant_id=10606 能命中黑名单。 功能完整闭环。
  • 后台 UI 已支持「不发货黑名单」模式(2026-08-12 完成)shopExpressTemplateDetailEdit.vue 已加「配送模式」三态 radio(0全国/1指定配送/2指定不发货);mode=2 复用 RegionMultiSelect 选地区,保存写 region_mode=2 + region_ids(城市码逗号串);回显按 region_mode 初始化。列表页 index.vue 的「配送地区」列按 region_mode 显示「不发货:xxx / 配送:xxx / 默认全国」,并把城市码聚合为省名。后端比对是编码集合 contains,故后台存城市码与 SQL 存省份码都正确拦截、互不冲突。
  • 仍可用 SQL 直接 INSERT 黑名单明细(region_mode=2, region_ids=偏远省码, status=0, tenant_id=10606)作为备选,无需经过 UI。
  • 接口方案文档:docs/运费模板-不发货地区-接口方案.md

shopGoods status 字段(上架/下架)

  • 0 = 已上架 / 上架
  • 1 = 待上架 / 下架
  • 2 = 待审核,3 = 审核不通过
  • 编辑表单「状态」radio 绑定 form.status0=上架、1=下架。
  • 一键上下架、批量上下架均通过 updateShopGoods({ ...record, status }) 直接改 status 实现(不额外用 isShow 字段)。
  • 列表 tag 仍显示:0=已上架、1=待上架、2=待审核、3=审核不通过。

运费模板配送地区多选(2026-08-11 已实现前端)

  • 需求:配送地区选择从「省+市单选级联」升级为「省+市两级多选穿梭弹窗」(仿客户截图双栏:左候选/右已选)。
  • 前端组件:src/components/RegionMultiSelect/index.vue(树形可展开,省级"选择"=选该省全部市,市级可单独选;@update:value 城市编码数组)。
  • 直接对接后端已有的 regionMode(0=全国/1=指定配送白名单/2=不发货黑名单) + regionIds(逗号分隔城市编码串) 机制,无需后端改动。城市编码即 regions-data.json 的 city value(6位),与 regionIds 完全一致。
  • 数据字段:model 用 regionMode / regionIds(已有字段,非新建);旧 provinceId/cityId 仅作回显兼容。
  • 列表多值展示:index.vue customRender 遍历 regionIds 反查 regionMap,超 22 字截断 + tooltip。
  • 下单校验 DeliveryRegionCheckerregionIds 直接生效(regionMode=2 为不发货黑名单)。
  • ⚠️ 教训:第一版误用 provinceIds/cityIds 字段名,与后端 regionMode/regionIds 不匹配,MyBatis-Plus 自动忽略未知字段导致"没存"。改字段名对接即可,不必新增后端字段。

专区白名单用户体系(2026-08-17 用户确认澄清)

  • 结论:本项目买家(小程序用户)就是 sys_user 体系,不存在用户体系错配。 之前怀疑"pageUsers(sys_user) vs 小程序买家(shop_user) 错配"是误判——pageShopUser(@/api/shop/shopUser) 在本项目实际不用/返回为空。
  • 白名单弹窗 UserSelectModal.vuepageUsers(@/api/system/user → sys_user) 选用户是正确的;存到 shop_home_section_user.user_id 与下单校验 checkPermission 用的 getLoginUser().getUserId() 同体系,能正确匹配。
  • 8-16 已去掉 isStaff:true 限制,白名单搜索覆盖全部 sys_user 用户(不局限于后台员工)。
  • ⚠️ 不要再把白名单弹窗数据源改成 pageShopUser(用户明确说 pageShopUser 没用)。
  • 后端下单白名单强校验(2026-08-17 已补)OrderBusinessService.createOrder 新增 validateSectionPermissionIfNeeded(request, shopOrder, loginUser),遍历订单商品的 section_ids 找受限专区(restricted=1),校验下单用户是否在 shop_home_section_user 白名单,不在则抛 "该商品属于受限专区,仅限指定白名单用户购买"。这是前端 check-permission 的后端兜底,防直接调下单接口绕过。
  • 前端 guilixu-taro pages/shop/checkout.tsx 已调用 check-permission 做前端拦截(受限专区强制余额/block)。
  • 当前状态:专区白名单功能完整可用(前端拦截 + 后端兜底)。另:专区管理菜单入口由后端菜单表下发(动态路由),若后台左侧导航看不到"专区管理",仍需在后端菜单表补一条记录(component=special/zone, tenant_id=10606)。

⚠️ ShopGoods.sectionIds 字段更新陷阱(MyBatis-Plus 字段策略)

  • ShopGoods.sectionIds@TableField(updateStrategy),走全局默认 NOT_NULLapplication.yml 未配 update-strategy)。
  • updateById/updateBatchByIdsection_ids 设为 null(清空所属专区)时,MP 忽略该 null 字段、不生成 SET,表现"更新成功但库未变"(假成功)。
  • 正确做法:清空/置空场景改用 UpdateWrapper.set("section_ids", newVal) 显式 setnewVal 可 null),绕过字段策略。
  • 已修复点:ShopHomeSectionController.addGoods/removeGoods(2026-08-17)改用此模式。同项目其它"置空某字段"的更新都要警惕此坑。

专区销量统计(2026-08-17 新增)

  • 需求:专区管理页 /special/zone 统计专区销量(件数 + 销售额),用户确认 方案B(订单真实成交聚合)+ 列表汇总列 + 独立统计弹窗
  • 后端(guilixu-java,仅此,无需 mp-java
    • ShopHomeSection 实体加 salesNum/salesAmount@TableField(exist=false) 统计字段,pageRel 分页后批量填充)。
    • 新增 VOSectionSalesSummaryVO(列表汇总)、ShopSectionGoodsRankVO(商品排行项)、SectionSalesStatsVO(含 List<ShopSectionGoodsRankVO> goodsRank)。
    • ShopHomeSectionMapper.xml 新增两条 JOIN 聚合 SQLselectSectionSalesSummary(批量,FIND_IN_SET(section_id, g.section_ids) 关联商品 → 聚合 shop_order_goods)、selectSectionGoodsRank(单专区 TOP50 排行)。
    • ShopHomeSectionController 新增 GET /shop/shop-home-section/{id}/stats?start=&end=
    • 统计口径shop_order_goods.pay_status=1(已付款) 且 order_status NOT IN (2已取消, 6退款成功);按 tenant_id 隔离;时间范围对应 shop_order_goods.create_timeyyyy-MM-dd HH:mm:ss)。
    • ⚠️ 订单商品表无 sectionId,专区销量=该专区 section_ids 关联商品的销量汇总;商品若属多个专区会被各专区重复计入(业务口径已知)。
  • 前端(guilixu-adminsrc/api/shop/shopZonegetSectionSalesStats + 类型;zone/index.vue 列表加「销量件数」「销售额」两列 + 操作列「销量」按钮;新建 src/views/special/zone/components/SectionSalesStatsModal.vue(时间范围 今日/近7天/近30天/自定义 + 汇总卡片 + 商品销量排行)。前端 vite build 已通过。