Files
guilixu-admin/.workbuddy/memory/MEMORY.md
T
gxwebsoft f986efce8c fix(api): 修复专区白名单和商品移除假成功问题
- 调整 .env.development 中 VITE_API_URL 配置为正式接口地址
- 修复 ShopGoods.sectionIds 字段更新时 null 被忽略导致移除专区商品假成功问题
- 通过 UpdateWrapper 显式设置 section_ids 字段避免 MyBatis-Plus 全局更新策略影响
- 补充后端下单接口白名单强校验,防止绕过前端受限专区限制
- 澄清买家用户体系为 sys_user,确认白名单弹窗使用 pageUsers 数据源正确
- 删除白名单弹窗 isStaff 限制,白名单覆盖所有 sys_user 用户
- 记录专区白名单功能设计与实现细节,明确前后端联动机制
- 提示“专区管理”菜单入口需后端菜单表动态下发,确保后台导航可见
2026-08-17 02:00:05 +08:00

83 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目长期记忆
## 项目结构
### 关键项目路径
- **管理后台**`/Users/gxwebsoft/VUE/guilixu-admin`Vue3 + Ant Design Vue
- **用户端小程序**`/Users/gxwebsoft/VUE/guilixu-taro`Taro + React
- **商家端小程序**`/Users/gxwebsoft/VUE/xinlong-shop-taro`Taro + 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.git``guilixu-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/request`baseURL = 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-taro``BaseUrl = https://shop-api.websoft.top/api`config/env.js 全环境),`TenantId='10606'`,下单 `POST /shop/shop-order` 即走 guilixu-java(db_guilixu);且单租户下买家 tenantId=10606(微信支付证书也依赖此值),故 `DeliveryRegionChecker``tenant_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.status`0=上架、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。
- 下单校验 `DeliveryRegionChecker``regionIds` 直接生效(regionMode=2 为不发货黑名单)。
- ⚠️ 教训:第一版误用 `provinceIds`/`cityIds` 字段名,与后端 `regionMode`/`regionIds` 不匹配,MyBatis-Plus 自动忽略未知字段导致"没存"。改字段名对接即可,不必新增后端字段。
### 专区白名单用户体系(2026-08-17 用户确认澄清)
- **结论:本项目买家(小程序用户)就是 `sys_user` 体系,不存在用户体系错配。** 之前怀疑"pageUsers(sys_user) vs 小程序买家(shop_user) 错配"是误判——`pageShopUser`(@/api/shop/shopUser) 在本项目实际不用/返回为空。
- 白名单弹窗 `UserSelectModal.vue``pageUsers`(@/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_NULL`application.yml 未配 `update-strategy`)。
-`updateById`/`updateBatchById``section_ids` 设为 `null`(清空所属专区)时,MP **忽略该 null 字段、不生成 SET**,表现"更新成功但库未变"(假成功)。
- **正确做法**:清空/置空场景改用 `UpdateWrapper.set("section_ids", newVal)` 显式 setnewVal 可 null),绕过字段策略。
- 已修复点:`ShopHomeSectionController.addGoods`/`removeGoods`(2026-08-17)改用此模式。同项目其它"置空某字段"的更新都要警惕此坑。