Files
guilixu-admin/.workbuddy/memory/2026-07-24.md
赵忠林 45fc45680f feat(passport): 增加多租户手机号登录支持并修复邮箱验证码校验
- 修复邮箱登录验证码未校验问题,恢复输入错误提示及刷新验证码逻辑
- 新增多租户选项登录流程,支持单租户直接登录及多租户选择弹窗
- 扩展登录接口及模型,新增 TenantOption 类型和 LoginOutcome 联合结果类型
- 新增 loginBySelectTenant 二次登录接口及对应组件逻辑
- 统一保存登录态,包括 token、租户ID和用户ID到本地存储
- 调整前端短信登录逻辑,根据返回状态决定流程,支持多租户弹窗选择
- 确保新增代码无类型错误并通过构建验证
2026-07-24 22:33:39 +08:00

82 lines
8.5 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.

# 2026-07-24 工作日志
## shopGoods 商品列表排序机制分析
- 现状:列表「排序号」列(`src/views/shop/shopGoods/index.vue` 约 273-279 行)只做展示,**未启用 `sorter`**,列表默认也不按 `sortNumber` 排序(默认排序由后端决定,前端 `orders` 仅来自「创建时间」列的排序)。
- 编辑表单里「排序号」是 `a-input-number`0-9999默认 100且在 `rules` 里是 **必填**
- 已存在类似的布尔开关 `recommend`列表内联切换index.vue 约 47-58 行),「置顶」可复用同一 UI 模式。
- 结论倾向:纯排序号在商品多时难用,建议「置顶开关 + 少量置顶项内排序 + 其余按默认(创建时间/销量)」的混合方案,而非完全替换。
## 已实现:方案 B商品置顶功能
- 数据模型 `src/api/shop/shopGoods/model/index.ts`:新增 `isTop?: number`(置顶 1/不置顶 0
- 列表 `src/views/shop/shopGoods/index.vue`
- 新增「置顶」列,列表内联一键切换(复用 recommend 的 CheckOutlined/CloseOutlined 模式),新增 `onTop` 处理函数。
- 「排序号」列开启 `sorter: true`(之前只展示、不可排序)。
- 后端迁移脚本 `add-is-top-shop-goods.sql`:新增 `is_top` TINYINT 字段;分页默认排序需改为 `ORDER BY is_top DESC, sort_number ASC, create_time DESC`(后端不在本仓库,需手动改 page 接口 ORDER BY / 放开 orders 白名单)。
- 注意:置顶真正"排到最前"依赖后端默认排序改动;点击列排序也需后端放开 is_top / sort_number。
## 后端改动(/Users/gxwebsoft/JAVA/guilixu-javaSpring Boot + MyBatis-Plus
- `entity/ShopGoods.java`:新增 `private Integer isTop;`(映射列 is_top沿用 camelCase→下划线策略mapper 用 SELECT a.*,无需改 XML
- `service/impl/ShopGoodsServiceImpl.java`
- `pageRel``listRel` 默认排序由 `sort_number asc, create_time desc` 改为 `is_top desc, sort_number asc, create_time desc`(置顶优先,置顶内按排序号,其余按创建时间倒序)。
- `collectFromUrl` 采集默认值补 `goods.setIsTop(0)`(与 recommend 一致)。
- 排序字段无白名单:`PageParam` 直接按列名拼 ORDER BY前端点「置顶」「排序号」列排序无需额外放开。
- DB 仍需执行 `add-is-top-shop-goods.sql`(在 admin 仓库根目录):`ALTER TABLE shop_goods ADD COLUMN is_top TINYINT(1) NOT NULL DEFAULT 0`
- 副作用提醒:默认排序改为置顶优先会影响所有调用 pageRel/listRel 的端(含小程序商品列表),置顶商品会浮到最前——通常正是期望行为。
## shopGoods 列表:商品编号移到名称下方
- `src/views/shop/shopGoods/index.vue`:删除独立的「编号」列(原 `code` 列),改为在「商品」列的名称下方显示「编号:{{ record.code }}」(灰色小字),节省一列横向空间。无 code 时不渲染该行。
## shopGoods 列表:三个价格合并为一列 + 与编辑弹窗对齐
- 原「市场价/价格/会员价」三列(字段 salePrice/price/dealerPrice合并为单个「价格」列`key: 'priceGroup'`width 150
- 单元格内堆叠显示,顺序与命名对齐编辑弹窗(`shopGoodsEdit.vue` 基本信息 Tab价格price加粗头条→ 市场价salePrice→ 会员价dealerPrice空值显示 `-`
- 列表展示用 priceGroup 自定义 `#bodyCell` 渲染;导出 Excel`handleExport`)仍保留三列独立价格,未改动。
## 商品分类(ShopGoodsCategory)多级分类调研
- 分类管理页 `/shop/shopGoodsCategory` 本身**支持**多级:模型有 parentId/children编辑弹窗用 a-tree-select(toTreeData) 不限层级,列表 computeDepth 递归缩进。
- 但商品端选分类的级联组件 `src/components/SelectGoodsCategory/index.vue` 的 formatData/filterData 只映射 2~3 级shopGoodsEdit 的 `chooseGoodsCategory` 写死 `value[1].value`/`value[0].label`(仅 2 级3 级及以上会选错/选不中。
- 后端 `/shop/shop-goods-category` 是否限层级需查后端仓库确认(本仓库看不到)。
## 新增:忘记密码(找回密码)功能
- 参考项目:`/Users/gxwebsoft/VUE/website-admin/app/pages/login.vue`(已验证可用)。
- 新增页面 `src/views/passport/forget/index.vue`:复用登录页视觉风格,支持「邮箱找回 / 手机找回」两种 Tab含验证码 60s 倒计时、密码强度校验≥8 位且含字母+数字)、两次密码一致性校验。
- 路由:在 `src/router/routes.ts` 启用 `/forget`(登录页「忘记密码」链接 `push('/forget')` 原本就指向它,路由此前被注释)。
- 新增 API
- `src/api/passport/login/index.ts``sendEmailCaptcha(data,{skipTenant})` → POST /sendEmailCaptcha`sendSmsCaptchaByAdmin(data,{skipTenant})` → POST /sendSmsCaptchaByAdmin。
- `src/api/layout/index.ts``resetPassword(data)` → POST /resetPassword带 X-Tenant-Skip:1`src/api/layout/model/index.ts` 新增 `ResetPasswordParam`
- 关键改动:`src/utils/request.ts` 请求拦截器在请求头含 `X-Tenant-Skip` 时跳过注入 TenantId/CompanyId/MerchantId/Domain登录前找回密码场景避免串租户`vite build --outDir dist-verify` 验证通过(真实 `vite build` 因沙箱 safe-delete 守卫删 dist 拦截,非代码问题)。
- 后端同址 server.websoft.top/api与 website-admin 共用一套找回密码接口。
## 优化:忘记密码页发送验证码前 3 次免图形验证
- 修改 `src/views/passport/forget/index.vue`:引入 `getCaptcha`,新增图形验证码弹窗(与 `register2/index.vue` 风格一致)。
-`localStorage``forget_send_count_${activeTab}_${account}` 记录每个邮箱/手机号的发送次数。
- 同一个账号前 3 次点击「获取验证码」直接调用 `sendEmailCaptcha`/`sendSmsCaptchaByAdmin`;第 4 次起弹出图形验证码弹窗,输入正确并点击「立即发送」后才真正发送。
- 失败时自动刷新图形验证码;弹窗关闭时重置 `codeLoading`
- 新增 i18n 键 `login.forgetPage.captchaTitle``captchaPlaceholder``sendNow`zh_CN/en
- `vue-tsc` 本次新增代码无类型错误;`vite build --outDir dist-verify-captcha` 编译通过。
## 优化:登录页手机号验证码前 3 次免图形验证
- 修改 `src/views/passport/login/index.vue`:手机号登录 Tab 下,「发送验证码」按钮由直接弹窗改为先计数判断。
-`localStorage``login_sms_send_count_${phone}` 记录每个手机号的发送次数。
- 同一个手机号前 3 次直接调用 `sendSmsCaptcha`;第 4 次起弹出原有图形验证码弹窗,输入正确并点击「立即发送」后才真正发送。
- 失败时自动刷新图形验证码;补充了 `.catch`/`.finally`,避免发送异常时按钮一直 loading。
- `vue-tsc` 本次新增代码无类型错误;`vite build --outDir dist-verify-login` 编译通过。
## 修复:邮箱登录图形验证码未校验
- **问题**`src/views/passport/login/index.vue` 第 651-555 行的验证码比对逻辑被注释掉,导致用户输入任意非空字符串即可通过前端校验。
- **修复**:取消注释 `form.code?.toLowerCase() !== text.value` 的校验块,恢复「输入错误则提示"验证码错误"并刷新验证码」的逻辑。
- **说明**:当前后端 `/captcha` 接口返回明文答案 `data.text`,前端直接比对即可。后续若后端改为返回 key 模式,需同步改为传 key 给后端校验。
## 新增:手机号登录多租户选择
- 参考项目:`/Users/gxwebsoft/VUE/website-admin/app/pages/login.vue`
- `src/api/passport/login/model/index.ts`
- `LoginResult` 新增 `tenants?: TenantOption[]`
- 新增 `TenantOption` 类型与 `LoginOutcome` 联合类型(`ok` / `selectTenant`)。
- `src/api/passport/login/index.ts`
- 新增 `saveLoginState` 统一保存 token、TenantId、UserId
- `loginBySuperAdminSms` 改为返回 `LoginOutcome`,检测 `data.tenants` 时返回 `selectTenant`
- 新增 `loginBySelectTenant({ phone, code, tenantId, remember })` 二次登录接口。
- `src/views/passport/login/index.vue`
- `onLoginBySms` 根据 `outcome.status` 处理:单租户直接登录成功;多租户弹出「请选择要登录的租户」弹窗。
- 新增弹窗与 `onSelectTenant`:选择租户后调用 `loginBySelectTenant` 完成二次登录并跳转。
- `vue-tsc` 本次新增代码无类型错误;`vite build --outDir dist-verify-tenant` 编译通过。