# 项目长期记忆 ## 项目结构 ### 关键项目路径 - **管理后台**:`/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.top(mp-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-java(guilixu-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)` 显式 set(newVal 可 null),绕过字段策略。 - 已修复点:`ShopHomeSectionController.addGoods`/`removeGoods`(2026-08-17)改用此模式。同项目其它"置空某字段"的更新都要警惕此坑。