Преглед на файлове

✨ feat(auth): 实现小程序静默登录与隐私政策流程

- 新增静默登录逻辑,自动获取用户openid并区分新老用户
- 优化首页登录流程,移除手动登录状态检查,改为自动静默登录
- 新增隐私政策弹框,仅对新用户显示并要求同意
- 重构用户认证逻辑,支持通过openid直接登录已注册用户
- 修改环境配置,更新API基础URL为测试环境地址

🐛 fix(debug): 调整调试日志输出级别

- 将地址编辑页面的console.debug改为console.log以便生产环境调试
- 移除首页多余的登录状态检查和调试日志
- 修复表单提交错误处理中的日志输出

♻️ refactor(payment): 优化支付回调租户配置

- 更新支付回调默认租户ID为13
- 增强支付服务日志,添加租户ID调试信息

🔧 chore(redis): 扩展会话管理功能

- 新增基于openid的session_key存储和获取方法
- 支持通过openid直接获取用户会话密钥
yourname преди 1 седмица
родител
ревизия
5e1f35556c

+ 2 - 1
mini/.env.production

@@ -2,7 +2,8 @@
 # TARO_APP_ID="生产环境下的小程序 AppID"
 
 # API配置
-TARO_APP_API_BASE_URL=https://api.yqingkj.com/
+TARO_APP_API_BASE_URL=https://d8d-ai-vscode-8080-186-175-template-22-group.r.d8d.fun
+# https://api.yqingkj.com/
 API_VERSION=v1
 
 # 租户ID

+ 7 - 7
mini/src/pages/address-edit/index.tsx

@@ -99,8 +99,8 @@ export default function AddressEditPage() {
 
       let response
 
-      //console.log("addressId:",addressId)
-      //console.log("addressData:",addressData)
+      console.log("addressId:",addressId)
+      console.log("addressData:",addressData)
 
       if (addressId) {
         response = await deliveryAddressClient[':id'].$put({
@@ -136,9 +136,9 @@ export default function AddressEditPage() {
 
 
   const onSubmit = (data: AddressFormData) => {
-    console.debug('表单提交数据:', data)
-    console.debug('表单验证状态:', form.formState.isValid)
-    console.debug('表单错误:', form.formState.errors)
+    console.log('表单提交数据:', data)
+    console.log('表单验证状态:', form.formState.isValid)
+    console.log('表单错误:', form.formState.errors)
     saveAddressMutation.mutate(data)
   }
 
@@ -235,10 +235,10 @@ export default function AddressEditPage() {
 
               <Button
                 className="w-full"
-                onClick={() => form.handleSubmit(onSubmit, (errors) => console.debug('表单验证错误:', errors))()}
+                onClick={() => form.handleSubmit(onSubmit, (errors) => console.log('表单验证错误:', errors))()}
                 disabled={saveAddressMutation.isPending}
               >
-                {saveAddressMutation.isPending ? '保存中...' : '保存地址'}
+               {saveAddressMutation.isPending ? '保存中...' : '保存地址'}
               </Button>
             </View>
           </Form>

+ 226 - 144
mini/src/pages/index/index.tsx

@@ -60,7 +60,6 @@ const HomePage: React.FC = () => {
   const { addToCart } = useCart();
   const [refreshing, setRefreshing] = React.useState(false);
   const [showPrivacyModal, setShowPrivacyModal] = React.useState(false);
-  const [isLoggingIn, setIsLoggingIn] = React.useState(false);
   
   // 广告数据查询
   const {
@@ -87,9 +86,6 @@ const HomePage: React.FC = () => {
     staleTime: 5 * 60 * 1000, // 5分钟缓存
   })
 
-  const hasToken = Taro.getStorageSync('mini_token')
-  console.debug('[首页] 商品查询状态:', { isLoggedIn, hasToken })
-
   const {
     data,
     isLoading,
@@ -101,7 +97,7 @@ const HomePage: React.FC = () => {
   } = useInfiniteQuery({
     queryKey: ['home-goods-infinite'],
     queryFn: async ({ pageParam = 1 }) => {
-      console.debug('[首页] 请求商品数据,页码:', pageParam)
+      // console.debug('请求商品数据,页码:', pageParam)
       // 使用类型断言绕过 RPC 客户端类型推断问题
       const response = await (goodsClient.$get as any)({
         query: {
@@ -112,16 +108,15 @@ const HomePage: React.FC = () => {
           sortOrder: 'DESC' // 倒序排列
         }
       })
-      console.debug('[首页] API响应状态:', response.status)
       if (response.status !== 200) {
         throw new Error('获取商品失败')
       }
       const result = await response.json()
-      console.debug('[首页] API响应数据:', {
-        page: pageParam,
-        dataCount: result.data?.length || 0,
-        pagination: result.pagination
-      })
+      // console.debug('API响应数据:', {
+      //   page: pageParam,
+      //   dataCount: result.data?.length || 0,
+      //   pagination: result.pagination
+      // })
       return result
     },
     getNextPageParam: (lastPage, _allPages) => {
@@ -143,7 +138,7 @@ const HomePage: React.FC = () => {
     },
     staleTime: 5 * 60 * 1000,
     initialPageParam: 1,
-    enabled: isLoggedIn || !!hasToken,
+    enabled: isLoggedIn,
   })
 
   // 合并所有分页数据
@@ -247,62 +242,151 @@ const HomePage: React.FC = () => {
     }
   })
 
-  // 未登录时显示隐私政策弹框(仅限从未登录过的用户)
+  // 未登录时先静默登录,检查是否是新用户
   React.useEffect(() => {
-    const hasToken = Taro.getStorageSync('mini_token')
-    // 只有完全没有登录过的用户才显示隐私弹框
-    if (!isLoggedIn && !hasToken) {
-      setShowPrivacyModal(true)
+    const checkUserAndShowPrivacy = async () => {
+      console.log("isLoggedIn:",isLoggedIn);
+      console.debug("isLoggedIn:",isLoggedIn);
+      if (!isLoggedIn) {
+        try {
+          // 获取登录code
+          const loginRes = await Taro.login()
+          console.log("loginRes")
+          console.log(loginRes)
+          if (!loginRes.code) {
+            console.error('获取登录凭证失败')
+            return
+          }
+
+          // 调用后端静默登录API
+          const response = await authClient['mini-login'].$post({
+            json: {
+              code: loginRes.code,
+              tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1
+            }
+          })
+
+          console.log("checkUserAndShowPrivacyResponse")
+          console.log(response)
+          if (response.status === 200) {
+            const { token, user, isNewUser } = await response.json()
+
+            // 保存token和用户信息
+            Taro.setStorageSync('userInfo', user)
+            Taro.setStorageSync('mini_token', token)
+            console.log("userInfo")
+            console.log(user)
+            setUser(user as any)
+            console.log("isNewUser")
+            console.log(isNewUser)
+            // 如果是新用户,显示隐私弹框
+            if (isNewUser) {
+              setShowPrivacyModal(true)
+            }
+          }
+        } catch (error) {
+          console.error('静默登录失败:', error)
+        }
+      }
     }
-  }, [isLoggedIn])
-
-  // 同意隐私政策,直接静默登录获取openid
-  const handleAgreePrivacy = async () => {
-    setIsLoggingIn(true)
-    try {
-      // 获取登录code
-      const loginRes = await Taro.login()
-      if (!loginRes.code) {
-        throw new Error('获取登录凭证失败')
+
+    checkUserAndShowPrivacy()
+  }, [isLoggedIn, setUser])
+
+  // 同意隐私政策,关闭弹框(已经登录过了)
+  const handleAgreePrivacy = async() => {
+    // setShowPrivacyModal(false)
+   
+    
+
+    // 调用后端静默登录API
+    // const response = await authClient['mini-login'].$post({
+    //   json: {
+    //     code: loginRes.code,
+    //     tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1
+    //   }
+    // })
+    const openid = Taro.getStorageSync('openid')
+    const response = await authClient['mini-login'].$post({
+      json: {
+        code: "1,"+openid,
+        tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1,
       }
+    })
 
-      // 调用后端静默登录API(不传userInfo)
-      const response = await authClient['mini-login'].$post({
-        json: {
-          code: loginRes.code,
-          tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1
+    if (response.status === 200) {
+      console.debug("response")
+      console.debug(response)
+      const { token, user, isNewUser } = await response.json();
+      // Taro.setStorageSync('openid', token);
+      Taro.setStorageSync('userInfo', JSON.stringify(user))
+
+      console.debug("token")
+      console.debug(token)
+      console.debug("user")
+      console.debug(user)
+      
+      console.debug("isNewUser")
+      console.debug(isNewUser)
+      if(isNewUser){
+
+      }else{
+        //直接登录
+        const { token: newToken } = await response.json()
+        Taro.setStorageSync('mini_token', newToken)
+
+        // 重新获取完整的用户信息
+        const meResponse = await authClient.me.$get({})
+        if (meResponse.status === 200) {
+          const userNew = await meResponse.json()
+          if(userNew ){
+            Taro.setStorageSync('userInfo', JSON.stringify(userNew))
+            console.debug('静默登录成功,返回新用户信息')
+            setShowPrivacyModal(false)  //隐藏框
+            //加载广告
+            handleRefresh()
+            return userNew
+          }else{
+            console.debug("静默登录失败")
+          }
         }
-      })
+       
+        //加载商品列表啊
+        
+        
+      }
+      // const { token: newToken } = await response.json()
+      // Taro.setStorageSync('mini_token', newToken)
+
+      // // 重新获取完整的用户信息
+      // const meResponse = await authClient.me.$get({})
+      // if (meResponse.status === 200) {
+      //   const user = await meResponse.json()
+      //   if(user.id!=null ){
+          // Taro.setStorageSync('userInfo', JSON.stringify(user))
+      //     console.debug('静默登录成功,返回新用户信息')
+      //     return user
+      //   }else{
+      //     //打开弹框
+      //     console.debug("静默登录失败")
+      //   }
+        
+      // }
+    }
 
-      if (response.status === 200) {
-        const { token, user } = await response.json()
+    Taro.showToast({ title: '欢迎使用', icon: 'success' })
 
-        // 保存token和用户信息
-        Taro.setStorageSync('userInfo', user)
-        Taro.setStorageSync('mini_token', token)
-        setUser(user as any)
 
-        setShowPrivacyModal(false)
-        Taro.showToast({ title: '登录成功', icon: 'success' })
-      } else {
-        throw new Error('登录失败')
-      }
-    } catch (error: any) {
-      console.error('静默登录失败:', error)
-      // 静默登录失败,跳转到登录页
-      setShowPrivacyModal(false)
-      Taro.navigateTo({ url: '/pages/login/index' })
-    } finally {
-      setIsLoggingIn(false)
-    }
   }
 
-  // 拒绝隐私政策,跳转到登录页
+  // 拒绝隐私政策,只关闭弹框
   const handleRejectPrivacy = () => {
     setShowPrivacyModal(false)
-    Taro.navigateTo({ url: '/pages/login/index' })
   }
 
+  // 如果未登录,不渲染页面内容
+  if (!isLoggedIn) return null
+
   // 跳转到商品详情
   const handleGoodsClick = (goods: GoodsData, index: number) => {
     console.log('点击商品:', goods, index)
@@ -385,107 +469,107 @@ const HomePage: React.FC = () => {
   return (
     <>
       <TabBarLayout activeKey="home">
-        <Navbar
-          title="首页"
-          leftIcon=""
-          onClickLeft={() => Taro.navigateBack()}
-          rightIcon=""
-          onClickRight={() => {}}
-        />
-        <ScrollView
-          className="home-scroll-view"
-          scrollY
-          refresherEnabled={true}
-          refresherTriggered={refreshing}
-          onRefresherRefresh={handleRefresh}
-          onScrollToLower={handleScrollToLower}      >
-          {/* 页面头部 - 搜索栏和轮播图 */}
-          <View className="home-page-header">
-            {/* 搜索栏 */}
-            <View className="search" onClick={handleSearchClick}>
-              <TDesignSearch
-                placeholder="搜索商品..."
-                disabled={true}
-                shape="round"
-              />
-            </View>
-
-            {/* 轮播图 */}
-            <View className="swiper-wrap">
-              {isAdLoading ? (
-                <View className="loading-container">
-                  <Text className="loading-text">广告加载中...</Text>
-                </View>
-              ) : adError ? (
-                <View className="error-container">
-                  <Text className="error-text">广告加载失败</Text>
-                </View>
-              ) : finalImgSrcs && finalImgSrcs.length > 0 ? (
-
-                <Carousel
-                  items={finalImgSrcs.filter((item: any) => item.imageFile?.fullUrl).map((item: any) => ({
-                    src: item.imageFile!.fullUrl,
-                    title: item.title || '',
-                    description: item.description || ''
-                  }))}
-                  height={800}
-                  autoplay={true}
-                  interval={3000}
-                  circular={true}
-                  imageMode="aspectFit"
-                />
-
-              )
-              : (
-                <View className="empty-container">
-                  <Text className="empty-text">暂无广告</Text>
-                </View>
-              )}
-            </View>
+      <Navbar
+        title="首页"
+        leftIcon=""
+        onClickLeft={() => Taro.navigateBack()}
+        rightIcon=""
+        onClickRight={() => {}}
+      />
+      <ScrollView
+        className="home-scroll-view"
+        scrollY
+        refresherEnabled={true}
+        refresherTriggered={refreshing}
+        onRefresherRefresh={handleRefresh}
+        onScrollToLower={handleScrollToLower}      >
+        {/* 页面头部 - 搜索栏和轮播图 */}
+        <View className="home-page-header">
+          {/* 搜索栏 */}
+          <View className="search" onClick={handleSearchClick}>
+            <TDesignSearch
+              placeholder="搜索商品..."
+              disabled={true}
+              shape="round"
+            />
           </View>
 
-          {/* 页面内容 - 商品列表 */}
-          <View className="home-page-container">
-            {isLoading ? (
+          {/* 轮播图 */}
+          <View className="swiper-wrap">
+            {isAdLoading ? (
               <View className="loading-container">
-                <Text className="loading-text">加载中...</Text>
+                <Text className="loading-text">广告加载中...</Text>
               </View>
-            ) : error ? (
+            ) : adError ? (
               <View className="error-container">
-                <Text className="error-text">加载失败,请重试</Text>
+                <Text className="error-text">广告加载失败</Text>
               </View>
-            ) : goodsList.length === 0 ? (
+            ) : finalImgSrcs && finalImgSrcs.length > 0 ? (
+            
+              <Carousel
+                items={finalImgSrcs.filter(item => item.imageFile?.fullUrl).map(item => ({
+                  src: item.imageFile!.fullUrl,
+                  title: item.title || '',
+                  description: (item as any).description || ''
+                }))}
+                height={800}
+                autoplay={true}
+                interval={4000}
+                circular={true}
+                imageMode="aspectFit"
+              />
+            
+            )
+            : (
               <View className="empty-container">
-                <Text className="empty-text">暂无商品</Text>
+                <Text className="empty-text">暂无广告</Text>
               </View>
-            ) : (
-              <>
-                <GoodsList
-                  goodsList={goodsList}
-                  onClick={handleGoodsClick}
-                  onAddCart={handleAddCart}
-                />
-
-                {/* 加载更多状态 */}
-                {isFetchingNextPage && (
-                  <View className="loading-more-container">
-                    <Text className="loading-more-text">加载更多...</Text>
-                  </View>
-                )}
-
-                {/* 无更多数据状态 */}
-                {!hasNextPage && goodsList.length > 0 && (
-                  <View className="no-more-container">
-                  <Text className="no-more-text">
-                    {`已经到底啦 (共${goodsList.length}件商品)`}
-                  </Text>
+            )}
+          </View>
+        </View>
+
+        {/* 页面内容 - 商品列表 */}
+        <View className="home-page-container">
+          {isLoading ? (
+            <View className="loading-container">
+              <Text className="loading-text">加载中...</Text>
+            </View>
+          ) : error ? (
+            <View className="error-container">
+              <Text className="error-text">加载失败,请重试</Text>
+            </View>
+          ) : goodsList.length === 0 ? (
+            <View className="empty-container">
+              <Text className="empty-text">暂无商品</Text>
+            </View>
+          ) : (
+            <>
+              <GoodsList
+                goodsList={goodsList}
+                onClick={handleGoodsClick}
+                onAddCart={handleAddCart}
+              />
+
+              {/* 加载更多状态 */}
+              {isFetchingNextPage && (
+                <View className="loading-more-container">
+                  <Text className="loading-more-text">加载更多...</Text>
                 </View>
               )}
+
+              {/* 无更多数据状态 */}
+              {!hasNextPage && goodsList.length > 0 && (
+                <View className="no-more-container">
+                <Text className="no-more-text">
+                  {`已经到底啦 (共${goodsList.length}件商品)`}
+                </Text>
+              </View>
+              )}
             </>
           )}
           <View className='height130'></View>
         </View>
-
+        
       </ScrollView>
     </TabBarLayout>
 
@@ -533,9 +617,7 @@ const HomePage: React.FC = () => {
                   className="flex-1 bg-green-500 text-white text-center py-3 rounded-lg"
                   onClick={handleAgreePrivacy}
                 >
-                  <Text className="text-white font-medium">
-                    {isLoggingIn ? '登录中...' : '同意'}
-                  </Text>
+                  <Text className="text-white font-medium">同意</Text>
                 </View>
               </View>
             </View>

+ 120 - 43
mini/src/utils/auth.tsx

@@ -41,6 +41,74 @@ export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
     return null
   })
 
+  // // 获取用户信息的通用函数
+  // const fetchUserInfo = async (): Promise<User | null> => {
+  //   // const token = Taro.getStorageSync('mini_token')
+  //   // if (!token) {
+  //   //   return null
+  //   // }
+  //   try {
+  //     const response = await authClient.me.$get({})
+  //     console.debug("response")
+  //     console.debug(response)
+  //     if (response.status !== 200) {
+  //       throw new Error('获取用户信息失败')
+  //     }
+  //     const user = await response.json()
+  //     Taro.setStorageSync('userInfo', JSON.stringify(user))
+  //     return user
+  //   } catch (error) {
+  //     // Token失效时的处理逻辑
+  //     console.debug('Token失效,开始处理重新登录逻辑')
+
+  //     if (isWeapp()) {
+  //       // 小程序环境:使用静默登录重新获取token
+  //       console.debug('小程序环境,尝试静默登录')
+  //       try {
+  //         const loginRes = await Taro.login()
+  //         if (loginRes.code) {
+  //           const response = await authClient['mini-login'].$post({
+  //             json: {
+  //               code: loginRes.code,
+  //               tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1
+  //             }
+  //           })
+
+  //           if (response.status === 200) {
+  //             const { token: newToken } = await response.json()
+  //             Taro.setStorageSync('mini_token', newToken)
+
+  //             // 重新获取完整的用户信息
+  //             const meResponse = await authClient.me.$get({})
+  //             if (meResponse.status === 200) {
+  //               const user = await meResponse.json()
+  //               if(user.id!=null ){
+  //                 Taro.setStorageSync('userInfo', JSON.stringify(user))
+  //                 console.debug('静默登录成功,返回新用户信息')
+  //                 return user
+  //               }else{
+  //                 console.debug("静默登录失败")
+  //               }
+                
+  //             }
+  //           }
+  //         }
+  //       } catch (silentLoginError) {
+  //         console.debug('静默登录失败:', silentLoginError)
+  //       }
+  //     } else if (isH5()) {
+  //       // H5环境:跳转到登录页
+  //       console.debug('H5环境,跳转到登录页')
+  //       Taro.redirectTo({ url: '/pages/login/index' })
+  //     }
+
+  //     // 如果重新登录失败或不是小程序环境,清除token并返回null
+  //     Taro.removeStorageSync('mini_token')
+  //     Taro.removeStorageSync('userInfo')
+  //     return null
+  //   }
+  // }
+
   // 获取用户信息的通用函数
   const fetchUserInfo = async (): Promise<User | null> => {
     // const token = Taro.getStorageSync('mini_token')
@@ -48,58 +116,65 @@ export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
     //   return null
     // }
     try {
-      const response = await authClient.me.$get({})
-      if (response.status !== 200) {
-        throw new Error('获取用户信息失败')
-      }
-      const user = await response.json()
-      Taro.setStorageSync('userInfo', JSON.stringify(user))
-      return user
-    } catch (error) {
-      // Token失效时的处理逻辑
-      console.debug('Token失效,开始处理重新登录逻辑')
-
-      if (isWeapp()) {
-        // 小程序环境:使用静默登录重新获取token
-        console.debug('小程序环境,尝试静默登录')
-        try {
-          const loginRes = await Taro.login()
-          if (loginRes.code) {
-            const response = await authClient['mini-login'].$post({
-              json: {
-                code: loginRes.code,
-                tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1
-              }
-            })
+      const loginRes = await Taro.login()
+      if (loginRes.code) {
+        const response = await authClient['mini-login'].$post({
+          json: {
+            code: loginRes.code,
+            tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1,
+            
+          }
+        })
 
-            if (response.status === 200) {
+        if (response.status === 200) {
+          console.debug("response")
+          console.debug(response)
+          const { token, user, isNewUser } = await response.json();
+          if(isNewUser){
+            Taro.setStorageSync('openid', token);
+          }else{
+            //直接登录
               const { token: newToken } = await response.json()
               Taro.setStorageSync('mini_token', newToken)
 
               // 重新获取完整的用户信息
               const meResponse = await authClient.me.$get({})
               if (meResponse.status === 200) {
-                const user = await meResponse.json()
-                Taro.setStorageSync('userInfo', JSON.stringify(user))
-                console.debug('静默登录成功,返回新用户信息')
-                return user
+                const userNew = await meResponse.json()
+                if(userNew ){
+                  Taro.setStorageSync('userInfo', JSON.stringify(userNew))
+                  console.debug('静默登录成功,返回新用户信息')
+                  return userNew
+                }else{
+                  console.debug("静默登录失败")
+                }
               }
-            }
           }
-        } catch (silentLoginError) {
-          console.debug('静默登录失败:', silentLoginError)
+          
+          // const user = await meResponse.json()
+          // Taro.setStorageSync('userInfo', JSON.stringify(user))
+          // const { token: newToken } = await response.json()
+          // Taro.setStorageSync('mini_token', newToken)
+
+          // // 重新获取完整的用户信息
+          // const meResponse = await authClient.me.$get({})
+          // if (meResponse.status === 200) {
+          //   const user = await meResponse.json()
+          //   if(user.id!=null ){
+         
+          //   }else{
+          //     //打开弹框
+          //     console.debug("静默登录失败")
+          //   }
+            
+          // }
         }
-      } else if (isH5()) {
-        // H5环境:跳转到登录页
-        console.debug('H5环境,跳转到登录页')
-        Taro.redirectTo({ url: '/pages/login/index' })
-      }
 
-      // 如果重新登录失败或不是小程序环境,清除token并返回null
-      Taro.removeStorageSync('mini_token')
-      Taro.removeStorageSync('userInfo')
-      return null
+      }
+    } catch (silentLoginError) {
+      console.debug('静默登录失败:', silentLoginError)
     }
+    return null
   }
 
   // 静默登录mutation - 移除自动登录,改为需要用户主动同意
@@ -119,9 +194,9 @@ export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
   // 移除自动静默登录逻辑 - 小程序启动时不再自动登录
   // 用户需要主动点击登录按钮并同意授权后才能登录
   // 如需恢复自动登录,取消以下注释:
-  // useLaunch(() => {
-  //   silentLoginMutation.mutate()
-  // })
+  useLaunch(() => {
+    silentLoginMutation.mutate()
+  })
 
 
   const loginMutation = useMutation<User, Error, LoginRequest>({
@@ -258,6 +333,8 @@ export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
 
 export const useAuth = () => {
   const context = useContext(AuthContext)
+  console.log("context:",context)
+  console.debug("context:",context)
   if (context === undefined) {
     throw new Error('useAuth must be used within an AuthProvider')
   }

+ 3 - 1
packages/auth-module/src/routes/mini-login.route.ts

@@ -54,10 +54,12 @@ const app = new OpenAPIHono().openapi(miniLoginRoute, async (c) => {
 
     const { code, userInfo } = c.req.valid('json');
 
+    
     const result = await miniAuthService.miniLogin(code);
+    console.debug("result:",result);
 
     // 如果有用户信息,更新用户资料
-    if (userInfo) {
+    if (userInfo && result.user.id!=null) {
       await miniAuthService.updateUserProfile(result.user.id, {
         nickname: userInfo.nickName,
         avatarUrl: userInfo.avatarUrl

+ 1 - 0
packages/auth-module/src/schemas/auth.schema.ts

@@ -32,6 +32,7 @@ export const MiniLoginSchema = z.object({
     example: '08123456789012345678901234567890',
     description: '小程序登录code'
   }),
+ 
   userInfo: z.object({
     nickName: z.string().optional(),
     avatarUrl: z.string().optional()

+ 66 - 19
packages/auth-module/src/services/mini-auth.service.ts

@@ -16,28 +16,57 @@ export class MiniAuthService {
 
   async miniLogin(code: string): Promise<{ token: string; user: UserEntity; isNewUser: boolean }> {
     // 1. 通过code获取openid和session_key
-    const openidInfo = await this.getOpenIdByCode(code);
-
-    // 2. 查找或创建用户
-    let user = await this.userRepository.findOne({
-      where: { openid: openidInfo.openid }
-    });
-
-    let isNewUser = false;
-
-    if (!user) {
-      // 自动注册新用户
-      user = await this.createMiniUser(openidInfo);
-      isNewUser = true;
+    console.debug("miniLogin:code"+code);
+    let data = code.split(",");
+    let openid = "";
+    if(data.length==2){
+      openid = data[1];
+      code = data[0];
     }
+    console.debug("openid")
+    if(openid!=""){
+      
 
-    // 3. 保存sessionKey到Redis
-    await redisUtil.setSessionKey(user.id, openidInfo.session_key);
+      const openidInfo= {
+        openid: openid
+      };
 
-    // 4. 生成token
-    const token = this.generateToken(user);
+      let user = await this.createMiniUser(openidInfo);
+      // 3. 保存sessionKey到Redis
+      let sessionKey = await redisUtil.getSessionKeyByOpenid(openid) ;
+
+      await redisUtil.setSessionKey(user.id,sessionKey || '');
+      let token = this.generateToken(user);
+      let isNewUser = false;
+      return { token, user, isNewUser };
+    }else{
+
+      // 1. 通过code获取openid和session_key
+      const openidInfo = await this.getOpenIdByCode(code);
+
+      // 2. 查找或创建用户
+      let user = await this.userRepository.findOne({
+        where: { openid: openidInfo.openid }
+      });
+
+      let isNewUser = false;
+      let token = "";
+
+      if (!user) {
+        // 自动注册新用户
+        user = await this.createMiniUserNoId(openidInfo);
+        isNewUser = true;
+        token = openid;
+        await redisUtil.setSessionKeyByOpenid(openid, openidInfo.session_key);
+      }else{
+        // 3. 保存sessionKey到Redis
+        await redisUtil.setSessionKey(user.id, openidInfo.session_key);
+        token = this.generateToken(user);
+        // isNewUser = true;
+      }
 
-    return { token, user, isNewUser };
+      return { token,user, isNewUser };
+    }
   }
 
   async updateUserProfile(userId: number, profile: { nickname?: string; avatarUrl?: string }): Promise<UserEntity> {
@@ -69,7 +98,9 @@ export class MiniAuthService {
   private async getOpenIdByCode(code: string): Promise<{ openid: string; unionid?: string; session_key: string }> {
     const appId = process.env.WX_MINI_APP_ID;
     const appSecret = process.env.WX_MINI_APP_SECRET;
-
+    console.log("appId")
+    console.log(appId)
+    console.log(appSecret)
     if (!appId || !appSecret) {
       throw new Error('微信小程序配置缺失');
     }
@@ -111,6 +142,22 @@ export class MiniAuthService {
     return await this.userRepository.save(user);
   }
 
+  private async createMiniUserNoId(openidInfo: { openid: string; unionid?: string }): Promise<UserEntity> {
+    const user = this.userRepository.create({
+      username: `wx_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
+      password: '', // 小程序用户不需要密码
+      openid: openidInfo.openid,
+      unionid: openidInfo.unionid,
+      nickname: '微信用户',
+      registrationSource: 'miniapp',
+      isDisabled: 0,
+      isDeleted: 0
+    });
+     
+    // await this.userRepository.save(user);
+    return user;
+  }
+
   private async downloadAndSaveAvatar(avatarUrl: string, userId: number): Promise<number | null> {
     try {
       const result = await this.fileService.downloadAndSaveFromUrl(

+ 63 - 17
packages/core-module-mt/auth-module-mt/src/services/mini-auth.service.mt.ts

@@ -22,29 +22,60 @@ export class MiniAuthService {
   }
 
   async miniLogin(code: string, tenantId?: number): Promise<{ token: string; user: UserEntityMt; isNewUser: boolean }> {
-    // 1. 通过code获取openid和session_key
-    const openidInfo = await this.getOpenIdByCode(code, tenantId);
+    console.debug("miniLogin:code"+code);
+    let data = code.split(",");
+    let openid = "";
+    if(data.length==2){
+      openid = data[1];
+      code = data[0];
+    }
+    if(openid!=""){
+      
 
-    // 2. 查找或创建用户
-    let user = await this.userRepository.findOne({
-      where: { openid: openidInfo.openid }
-    });
+      const openidInfo= {
+        openid: openid
+      };
 
-    let isNewUser = false;
+      let user = await this.createMiniUser(openidInfo, tenantId);
+      // 3. 保存sessionKey到Redis
+      let sessionKey = await redisUtil.getSessionKeyByOpenid(openid) ;
 
-    if (!user) {
-      // 自动注册新用户
-      user = await this.createMiniUser(openidInfo, tenantId);
-      isNewUser = true;
-    }
+      await redisUtil.setSessionKey(user.id,sessionKey || '');
+      let token = this.generateToken(user);
+      let isNewUser = false;
+      return { token, user, isNewUser };
+    }else{
+
+      // 1. 通过code获取openid和session_key
+      const openidInfo = await this.getOpenIdByCode(code, tenantId);
+
+      // 2. 查找或创建用户
+      let user = await this.userRepository.findOne({
+        where: { openid: openidInfo.openid }
+      });
+
+      let isNewUser = false;
+      let token = "";
+
+      if (!user) {
+        // 自动注册新用户
+        user = await this.createMiniUserNoId(openidInfo, tenantId);
+        isNewUser = true;
+        token = openidInfo.openid;
+        await redisUtil.setSessionKeyByOpenid(openid, openidInfo.session_key);
+      }else{
+        // 3. 保存sessionKey到Redis
+        await redisUtil.setSessionKey(user.id, openidInfo.session_key);
+        token = this.generateToken(user);
+        // isNewUser = true;
+      }
 
-    // 3. 保存sessionKey到Redis
-    await redisUtil.setSessionKey(user.id, openidInfo.session_key);
+      openid = openidInfo.openid;
 
-    // 4. 生成token
-    const token = this.generateToken(user);
+      return { token,user, isNewUser };
+
+    }
 
-    return { token, user, isNewUser };
   }
 
   async updateUserProfile(userId: number, profile: { nickname?: string; avatarUrl?: string }): Promise<UserEntityMt> {
@@ -135,7 +166,22 @@ export class MiniAuthService {
 
     return await this.userRepository.save(user);
   }
+  
+  private async createMiniUserNoId(openidInfo: { openid: string; unionid?: string }, tenantId?: number): Promise<UserEntityMt> {
+    const user = this.userRepository.create({
+      tenantId: tenantId || 1, // 默认租户ID为1,如果未提供
+      username: `wx_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
+      password: '', // 小程序用户不需要密码
+      openid: openidInfo.openid,
+      unionid: openidInfo.unionid,
+      nickname: '微信用户',
+      registrationSource: 'miniapp',
+      isDisabled: 0,
+      isDeleted: 0
+    });
 
+    return user;
+  }
   private async downloadAndSaveAvatar(avatarUrl: string, userId: number): Promise<number | null> {
     try {
       const result = await this.fileService.downloadAndSaveFromUrl(

+ 1 - 1
packages/mini-payment-mt/src/routes/payment/callback.mt.ts

@@ -44,7 +44,7 @@ const app = new OpenAPIHono()
       // 获取微信支付回调头信息
       const headers = {
         // 开发环境配置成 租户2 'X-Tenant-Id': '2'
-        'X-Tenant-Id': c.req.header('X-Tenant-Id') || '2',
+        'X-Tenant-Id': c.req.header('X-Tenant-Id') || '13',
         'wechatpay-timestamp': c.req.header('wechatpay-timestamp') || '',
         'wechatpay-nonce': c.req.header('wechatpay-nonce') || '',
         'wechatpay-signature': c.req.header('wechatpay-signature') || '',

+ 1 - 0
packages/mini-payment-mt/src/services/payment.mt.service.ts

@@ -311,6 +311,7 @@ export class PaymentMtService extends GenericCrudService<PaymentMtEntity> {
 
     // 重新初始化微信支付SDK(先初始化以进行签名验证和解密)
     console.debug(`开始初始化微信支付SDK`);
+    console.debug("headers['X-Tenant-Id']:",headers['X-Tenant-Id']);
     await this.initializeWxPay(headers['X-Tenant-Id']); // 使用从域名代理中配置的header中的租户id
     console.debug(`微信支付SDK初始化完成`);
 

+ 12 - 1
packages/shared-utils/src/utils/redis.util.ts

@@ -35,6 +35,13 @@ class RedisUtil {
     }
   }
 
+  async setSessionKeyByOpenid(openid: string, sessionKey: string, ttlSeconds: number = 7200): Promise<void> {
+    const client = await this.connect();
+    const key = `session_key:${openid}`;
+    await client.set(key, sessionKey, {
+      EX: ttlSeconds // 默认2小时过期,与微信session_key有效期一致
+    });
+  }
   async setSessionKey(userId: number, sessionKey: string, ttlSeconds: number = 7200): Promise<void> {
     const client = await this.connect();
     const key = `session_key:${userId}`;
@@ -48,7 +55,11 @@ class RedisUtil {
     const key = `session_key:${userId}`;
     return await client.get(key);
   }
-
+  async getSessionKeyByOpenid(openid: string): Promise<string | null> {
+    const client = await this.connect();
+    const key = `session_key:${openid}`;
+    return await client.get(key);
+  }
   async deleteSessionKey(userId: number): Promise<void> {
     const client = await this.connect();
     const key = `session_key:${userId}`;