feat(user): 补充开发者审核通过时添加店员记录功能

- 引入 addShopStoreUser 接口用于新增门店店员记录
- 新增 clerkStoreId 和 clerkTenantId 状态,保存当前审核店员的门店和租户信息
- 在审核通过后调用 addShopStoreUser,添加申请人为当前门店店员
- 保证只有当 clerkStoreId 存在时才执行新增操作
- 更新工作记录文档,补充审核通过流程中新添加的店员记录步骤
- 保持原有升级开发者逻辑和接口调用不变
This commit is contained in:
2026-07-23 17:59:22 +08:00
parent a703dcad90
commit babcf36439
4 changed files with 40 additions and 13 deletions

20
.idea/workspace.xml generated
View File

@@ -116,15 +116,7 @@
<workItem from="1784133736973" duration="4614000" /> <workItem from="1784133736973" duration="4614000" />
<workItem from="1784585070286" duration="3047000" /> <workItem from="1784585070286" duration="3047000" />
<workItem from="1784638429525" duration="16147000" /> <workItem from="1784638429525" duration="16147000" />
<workItem from="1784798575885" duration="656000" /> <workItem from="1784798575885" duration="986000" />
</task>
<task id="LOCAL-00019" summary="feat(passport): 重写登录与邀请加入流程,支持微信手机号一键登录&#10;&#10;- 完全重写 passport/login.tsx实现微信手机号一键登录功能&#10;- 支持登录页面携带 redirect 参数,登录后自动跳转回原页面&#10;- 登录成功后自动处理邀请加入应用,支持邀请关系绑定&#10;- 重写 passport/invite/index.tsx实现扫码邀请加入应用完整流程&#10;- 支持调用 wx.login 获取 code使用 loginByOpenId 判断注册状态&#10;- 未注册用户自动跳转登录页完成微信手机号登录再加入应用&#10;- 已登录用户直接执行加入应用操作,加入接口支持 Authorization 头&#10;- 解决邀请页面与登录页面间旧授权码混用导致加入失败问题&#10;- 处理后端 401 错误,优先使用微信授权码免登录接口&#10;- 优化用户体验,添加服务协议与隐私政策弹窗及错误提示&#10;- 页面状态管理改进,加入加载、检查登录、错误和跳转反馈">
<option name="closed" value="true" />
<created>1775962292970</created>
<option name="number" value="00019" />
<option name="presentableId" value="LOCAL-00019" />
<option name="project" value="LOCAL" />
<updated>1775962292970</updated>
</task> </task>
<task id="LOCAL-00020" summary="feat(invite): 重构邀请加入流程,实现已登录用户免授权直接加入&#10;&#10;- 完全重写邀请页逻辑,区分已登录与未注册用户按钮显示&#10;- 已登录用户显示“确认加入”按钮,点击用 wx.login 获取 code 加入&#10;- 未注册用户显示“微信手机号快速加入”按钮,授权手机号后完成注册登录再加入&#10;- 移除跳转登录页面逻辑,所有流程在邀请页内完成&#10;- 加入接口统一使用 Authorization 头携带 token确保身份双重验证&#10;- 优化背景、粒子和扫描线效果,提升界面视觉体验&#10;- 增加错误处理提示,加入失败时显示详细原因并可返回首页&#10;- 协议勾选仅在未注册用户显示,确保合规授权流程"> <task id="LOCAL-00020" summary="feat(invite): 重构邀请加入流程,实现已登录用户免授权直接加入&#10;&#10;- 完全重写邀请页逻辑,区分已登录与未注册用户按钮显示&#10;- 已登录用户显示“确认加入”按钮,点击用 wx.login 获取 code 加入&#10;- 未注册用户显示“微信手机号快速加入”按钮,授权手机号后完成注册登录再加入&#10;- 移除跳转登录页面逻辑,所有流程在邀请页内完成&#10;- 加入接口统一使用 Authorization 头携带 token确保身份双重验证&#10;- 优化背景、粒子和扫描线效果,提升界面视觉体验&#10;- 增加错误处理提示,加入失败时显示详细原因并可返回首页&#10;- 协议勾选仅在未注册用户显示,确保合规授权流程">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -510,7 +502,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1784799951929</updated> <updated>1784799951929</updated>
</task> </task>
<option name="localTasksCounter" value="68" /> <task id="LOCAL-00068" summary="fix(weapp): 修复支付页nutui组件导致内容空白问题&#10;&#10;- 替换支付页中nutui的Button、Cell、Price、Divider组件为Taro原生组件或项目自定义组件&#10;- 修正商品名称、价格类型和支付金额显示使用flex布局及Text组件自绘内容&#10;- 调整按钮样式,统一为带蓝色背景和圆角的自定义样式&#10;- 移除nutui Price组件中的thousands属性使用项目自定义Price组件&#10;- 解决微信小程序中nutui组件因div渲染缺失导致UI元素不显示的问题&#10;- 保持支付流程及状态逻辑无改动,仅优化展示兼容性和稳定性">
<option name="closed" value="true" />
<created>1784800111045</created>
<option name="number" value="00068" />
<option name="presentableId" value="LOCAL-00068" />
<option name="project" value="LOCAL" />
<updated>1784800111045</updated>
</task>
<option name="localTasksCounter" value="69" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">

View File

@@ -21,3 +21,14 @@
- 文件最终 413 行diff 集中在 import + 渲染区逻辑数据获取、JSAPI 支付、状态判断 payStatus vs status没动 - 文件最终 413 行diff 集中在 import + 渲染区逻辑数据获取、JSAPI 支付、状态判断 payStatus vs status没动
**遗留**项目其它页面passport/{login,forget,register,setting,unified-qr,qr-login}、pages/order/*、pages/booking/*、components/common/{FixedButton,EmptyState}、components/QRLoginScanner也用了 nutui Button/Checkbox/Input/Card/Tabs/Tag 等。如果以后用户反馈"按钮/输入框看不到或不可点",按同样模式替换即可。这次没改,避免一次改动面太大。 **遗留**项目其它页面passport/{login,forget,register,setting,unified-qr,qr-login}、pages/order/*、pages/booking/*、components/common/{FixedButton,EmptyState}、components/QRLoginScanner也用了 nutui Button/Checkbox/Input/Card/Tabs/Tag 等。如果以后用户反馈"按钮/输入框看不到或不可点",按同样模式替换即可。这次没改,避免一次改动面太大。
## vip-review 审核通过补充:添加 shop_store_user 记录
**背景**:之前实现的 `src/pages/user/vip-review/index.tsx` 审核通过只调了 `updateShopDealerApply` + `setUserDeveloper`,漏了"给申请人加一条门店店员记录"。用户 2026-07-23 指出"审核通过的操作,还是要添加一条记录到 shopStoreUser可能之前理解错了",要求补上。
**修改**(仅前端一个文件):
- import 增加 `addShopStoreUser`(来自 `@/api/shop/shopStoreUser`)。
- 新增 state `clerkStoreId` / `clerkTenantId`,在 `getMyClerk()` 成功后写入(店员记录自带 `storeId`/`tenantId`)。
- `handleApprove``setUserDeveloper` 之后调 `addShopStoreUser({ userId, storeId: clerkStoreId, tenantId: clerkTenantId, isDelete: 0, comments: '开发者审核通过自动添加' })`,仅当 `clerkStoreId` 存在时执行。
**依赖**:后端 `POST /shop/shop-store-user`ShopStoreUserController.save`shop:shopStoreUser:save` 权限店员已有tenantId 复用审核店员的租户,避免前端另取。

View File

@@ -7,7 +7,7 @@
- 后端仓库:`/Users/gxwebsoft/JAVA/com.gxwebsoft.core`(单 Maven 模块,包名 `com.gxwebsoft` 分包)。 - 后端仓库:`/Users/gxwebsoft/JAVA/com.gxwebsoft.core`(单 Maven 模块,包名 `com.gxwebsoft` 分包)。
- 标记接口:`PUT /api/system/user/developer/{userId}``UserController.setDeveloper``UserService.markAsDeveloper`),成功后经 `syncMessageProducer` 同步 websopy。 - 标记接口:`PUT /api/system/user/developer/{userId}``UserController.setDeveloper``UserService.markAsDeveloper`),成功后经 `syncMessageProducer` 同步 websopy。
- 前端调用:`@/api/system/user``setUserDeveloper(userId)` - 前端调用:`@/api/system/user``setUserDeveloper(userId)`
- **vip-review 审核通过流程(用户 2026-07-22 最终确认)**:前端 `handleApprove` 只调 `setUserDeveloper(userId)`(不碰 `sys_user_role`)。后端 `markAsDeveloper` 做两件事:① 把 `sys_user.is_developer``1`;② **替换**用户角色为 `developer`——`roleService.getByRoleCode(roleCode=developer, tenantId=用户租户)` 找到角色 → 删掉该用户现有 `sys_user_role` → 只绑定 developer 这一个角色,**不与原角色并存**(角色替换包 try/catch失败不影响 isDeveloper 落库)。即"升级为开发者 = 单角色 developer + isDeveloper 标记位",不是新增第二个角色。 - **vip-review 审核通过流程(用户 2026-07-22 最终确认2026-07-23 补充**:前端 `handleApprove` 依次调 ① `updateShopDealerApply(applyStatus=20)`;② `setUserDeveloper(userId)`(不碰 `sys_user_role`;③ **`addShopStoreUser`**——在当前门店下给申请人补一条 `shop_store_user`(店员)记录(`userId=申请人, storeId=审核店员所属门店, tenantId=同店员, isDelete=0`。后端 `markAsDeveloper` 做两件事:① 把 `sys_user.is_developer``1`;② **替换**用户角色为 `developer`——`roleService.getByRoleCode(roleCode=developer, tenantId=用户租户)` 找到角色 → 删掉该用户现有 `sys_user_role` → 只绑定 developer 这一个角色,**不与原角色并存**(角色替换包 try/catch失败不影响 isDeveloper 落库)。即"升级为开发者 = 单角色 developer + isDeveloper 标记位 + 一条门店店员记录",不是新增第二个角色。审核店员的 `storeId/tenantId` 来自 `getMyClerk()` 返回值(存于 `clerkStoreId/clerkTenantId` state
- 注意:前端 `vip-review` / `vip-upgrade` 等页面的变量名仍残留 `VIP_ROLE_*``pendingVipCount``dealerPrice` 等旧标识符(仅文案改成了「开发者」),后端角色与缓存 key 仍对应这些旧名,重命名需谨慎。 - 注意:前端 `vip-review` / `vip-upgrade` 等页面的变量名仍残留 `VIP_ROLE_*``pendingVipCount``dealerPrice` 等旧标识符(仅文案改成了「开发者」),后端角色与缓存 key 仍对应这些旧名,重命名需谨慎。
### 后端代码风格com.gxwebsoft.core ### 后端代码风格com.gxwebsoft.core

View File

@@ -4,7 +4,7 @@ import Taro, { useDidShow } from '@tarojs/taro'
import { listShopDealerApply, updateShopDealerApply } from '@/api/shop/shopDealerApply' import { listShopDealerApply, updateShopDealerApply } from '@/api/shop/shopDealerApply'
import type { ShopDealerApply } from '@/api/shop/shopDealerApply/model' import type { ShopDealerApply } from '@/api/shop/shopDealerApply/model'
import { setUserDeveloper } from '@/api/system/user' import { setUserDeveloper } from '@/api/system/user'
import { getMyClerk } from '@/api/shop/shopStoreUser' import { getMyClerk, addShopStoreUser } from '@/api/shop/shopStoreUser'
definePageConfig({ definePageConfig({
navigationBarTitleText: '开发者审核', navigationBarTitleText: '开发者审核',
@@ -44,6 +44,8 @@ const VipReviewPage: React.FC = () => {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [submitting, setSubmitting] = useState(false) const [submitting, setSubmitting] = useState(false)
const [isClerk, setIsClerk] = useState(false) const [isClerk, setIsClerk] = useState(false)
const [clerkStoreId, setClerkStoreId] = useState<number>()
const [clerkTenantId, setClerkTenantId] = useState<number>()
const loadingRef = useRef(false) const loadingRef = useRef(false)
// 验证当前用户是否为店员 // 验证当前用户是否为店员
@@ -52,6 +54,9 @@ const VipReviewPage: React.FC = () => {
.then(data => { .then(data => {
if (data) { if (data) {
setIsClerk(true) setIsClerk(true)
// 记录当前店员所属门店与租户,审核通过时用于给申请人添加店员记录
setClerkStoreId(data.storeId)
setClerkTenantId(data.tenantId)
} else { } else {
setIsClerk(false) setIsClerk(false)
Taro.showToast({ title: '仅门店店员可访问', icon: 'none' }) Taro.showToast({ title: '仅门店店员可访问', icon: 'none' })
@@ -132,6 +137,17 @@ const VipReviewPage: React.FC = () => {
// 审核通过:升级为开发者(后端 markAsDeveloper 会置 is_developer=1 并把角色替换为 developer // 审核通过:升级为开发者(后端 markAsDeveloper 会置 is_developer=1 并把角色替换为 developer
await setUserDeveloper(item.userId!) await setUserDeveloper(item.userId!)
// 审核通过在当前门店下给申请人添加一条店员shop_store_user记录
if (clerkStoreId) {
await addShopStoreUser({
userId: item.userId!,
storeId: clerkStoreId,
tenantId: clerkTenantId,
isDelete: 0,
comments: '开发者审核通过自动添加',
})
}
Taro.showToast({ title: '已通过', icon: 'success' }) Taro.showToast({ title: '已通过', icon: 'success' })
loadList(activeTab) loadList(activeTab)
} catch (e: any) { } catch (e: any) {