Просмотр исходного кода

♻️ refactor(profile): 调整个人中心页面布局与优化订单卡片显示顺序

- 将订单状态卡片移至欠款信息卡片下方,优化页面信息层级
- 更新欠款卡片中的图标从"📊"改为"💰",使其更符合语义
- 移除订单卡片与欠款卡片之间的多余空行,保持代码整洁

📝 docs(user-schema): 更新用户更新接口文档说明

- 在用户更新DTO的密码字段描述中明确说明"留空则不修改"
- 统一更新单租户和多租户用户模块的密码字段文档说明

🗑️ chore(env): 删除小程序开发环境示例配置文件

- 移除mini/.env.development.example文件,该文件已不再需要

🔧 refactor(order-management): 优化发货逻辑与清理注释代码

- 根据支付类型决定是否调用微信小程序发货信息录入API
- 信用支付订单(payType=3)跳过微信小程序发货信息录入
- 移除已注释的旧发货处理代码和微信小店发货相关代码
- 清理调试日志和冗余注释,保持代码简洁

✨ feat(user-management): 增强用户管理表单处理与错误处理

- 优化编辑用户表单初始化逻辑,正确处理null/undefined值
- 增强创建用户、更新用户和删除用户的错误处理机制
- 添加详细的调试日志,便于问题排查
- 实现表单数据验证、修剪和空值过滤
- 特殊处理密码字段,支持留空不修改功能
- 添加邮箱格式验证和用户名长度验证
- 优化响应状态码处理和错误信息展示

🔧 chore(web): 更新应用默认名称

- 将web应用的默认名称从"多八多Aider"改为"长愿青"
yourname 1 месяц назад
Родитель
Сommit
9f1f728cb3

+ 0 - 10
mini/.env.development.example

@@ -1,10 +0,0 @@
-# 配置文档参考 https://taro-docs.jd.com/docs/next/env-mode-config
-# TARO_APP_ID="开发环境下的小程序 AppID"
-
-# API配置
-# 需换成当前项目的
-TARO_APP_API_BASE_URL=https://d8d-ai-vscode-8080-186-175-template-6-group.r.d8d.fun
-TARO_APP_API_VERSION=v1
-
-# 租户ID
-TARO_APP_TENANT_ID=1

+ 18 - 13
mini/src/pages/profile/index.tsx

@@ -264,19 +264,10 @@ const ProfilePage: React.FC = () => {
 
       {/* 内容区域 - 使用 margin-top 定位 */}
       <ScrollView className="flex-1 bg-gray-50 tdesign-user-center-content z-10">
-        {/* 订单状态卡片 */}
-        <View className="px-4 pt-4">
-          <TDesignOrderGroup
-            orderTagInfos={orderTagInfos}
-            title="我的订单"
-            desc="全部订单"
-            onTopClick={() => handleNavigateToOrderList()}
-            onItemClick={(item) => handleNavigateToOrderList(item.tabKey)}
-          />
-        </View>
 
-        {/* 欠款信息卡片 */}
-        {creditBalance && creditBalance.usedAmount > 0 && (
+
+           {/* 欠款信息卡片 */}
+           {creditBalance && creditBalance.usedAmount > 0 && (
           <View className="px-4 pt-4">
             <View className="bg-white rounded-2xl overflow-hidden">
               <View className="p-5 border-b border-gray-100">
@@ -319,7 +310,7 @@ const ProfilePage: React.FC = () => {
                   <View className="flex items-center justify-between">
                     <View className="flex items-center">
                       <View className="w-10 h-10 bg-red-100 rounded-full flex items-center justify-center mr-3">
-                        <Text className="text-red-600 text-lg">📊</Text>
+                        <Text className="text-red-600 text-lg">💰</Text>
                       </View>
                       <View>
                         <Text className="text-sm text-gray-600">累计欠款</Text>
@@ -339,6 +330,20 @@ const ProfilePage: React.FC = () => {
           </View>
         )}
 
+
+
+        {/* 订单状态卡片 */}
+        <View className="px-4 pt-4">
+          <TDesignOrderGroup
+            orderTagInfos={orderTagInfos}
+            title="我的订单"
+            desc="全部订单"
+            onTopClick={() => handleNavigateToOrderList()}
+            onItemClick={(item) => handleNavigateToOrderList(item.tabKey)}
+          />
+        </View>
+
+     
         {/* 功能菜单 */}
         <View className="px-4 pt-4">
           {menuData.map((group, groupIndex) => (

+ 1 - 1
packages/core-module-mt/user-module-mt/src/schemas/user.schema.mt.ts

@@ -129,7 +129,7 @@ export const UpdateUserDtoMt = z.object({
   }),
   password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').optional().openapi({
     example: 'password123',
-    description: '密码,最少6位'
+    description: '密码,最少6位(留空则不修改)'
   }),
   phone: z.string().max(255, '手机号最多255个字符').nullable().optional().openapi({
     example: '13800138000',

+ 19 - 63
packages/order-management-ui-mt/src/components/OrderManagement.tsx

@@ -697,32 +697,6 @@ export const OrderManagement = () => {
     setDeliveryModalOpen(true);
   };
 
-  // 处理发货提交
-  // const handleDeliverySubmit = async (data: DeliveryRequest) => {
-  //   if (!deliveringOrder || !deliveringOrder.id) return;
-
-  //   try {
-  //     const res = await (orderClientManager.getAdminDeliveryClient() as any)[':id']['delivery']['$post']({
-  //       param: { id: deliveringOrder.id },
-  //       json: data,
-  //     });
-
-  //     if (res.status === 200) {
-  //       const result = await res.json() as DeliveryResponse;
-  //       toast.success(result.message || '发货成功');
-  //       setDeliveryModalOpen(false);
-  //       refetch();
-  //     } else {
-  //       const error = await res.json();
-  //       toast.error(error.message || '发货失败');
-  //     }
-  //   } catch (error) {
-  //     console.error('发货失败:', error);
-  //     toast.error('发货失败,请重试');
-  //   }
-  // };
-
-
 
   const handleDeliverySubmit = async (data: DeliveryRequest) => {
     if (!deliveringOrder || !deliveringOrder.id) {
@@ -803,48 +777,30 @@ export const OrderManagement = () => {
         toast.success(result.message || '发货成功');
         setDeliveryModalOpen(false);
         refetch();
-        // 发货成功后调用微信小程序发货信息录入API
-        try {
-          const uploadResult = await uploadShippingInfoToWechat(deliveringOrder, data, deliveringOrder?.tenantId);
 
-          if (uploadResult.success) {
-            console.debug("微信小程序发货信息录入成功:", uploadResult);
-            // 可以在这里添加额外的成功处理,比如记录日志
-          } else {
-            // 微信小程序发货信息录入失败,记录警告但不影响主流程
-            console.warn("微信小程序发货信息录入失败,但系统发货成功:", uploadResult);
-            // 可以在这里添加额外的处理,比如记录到日志系统或发送告警
+        // 发货成功后根据支付类型决定是否调用微信小程序发货信息录入API
+        // 信用支付(payType === 3)不调用,正常支付(payType !== 3)调用
+        if (deliveringOrder?.payType !== 3) {
+          try {
+            const uploadResult = await uploadShippingInfoToWechat(deliveringOrder, data, deliveringOrder?.tenantId);
+
+            if (uploadResult.success) {
+              console.debug("微信小程序发货信息录入成功:", uploadResult);
+              // 可以在这里添加额外的成功处理,比如记录日志
+            } else {
+              // 微信小程序发货信息录入失败,记录警告但不影响主流程
+              console.warn("微信小程序发货信息录入失败,但系统发货成功:", uploadResult);
+              // 可以在这里添加额外的处理,比如记录到日志系统或发送告警
+            }
+          } catch (uploadError) {
+            console.error("调用微信小程序发货信息录入API时发生异常:", uploadError);
+            // 不阻止发货成功,只记录错误
           }
-        } catch (uploadError) {
-          console.error("调用微信小程序发货信息录入API时发生异常:", uploadError);
-          // 不阻止发货成功,只记录错误
+        } else {
+          console.debug("信用支付订单(payType=3),跳过微信小程序发货信息录入");
         }
 
 
-        
-
-
-        // 发货成功后调用微信小店发货API
-        // try {
-        //   const wechatDeliveryResult = await sendWechatShopDelivery(deliveringOrder, data);
-
-        //   if (wechatDeliveryResult.success) {
-        //     console.debug('微信小店发货成功:', wechatDeliveryResult);
-        //     // 可以在这里添加额外的成功处理,比如记录日志
-        //   } else {
-        //     // 微信小店发货失败,记录警告但不影响主流程
-        //     console.warn('微信小店发货失败,但系统发货成功:', wechatDeliveryResult);
-        //     // 可以在这里添加额外的处理,比如记录到日志系统或发送告警
-        //   }
-        // } catch (wechatDeliveryError) {
-        //   console.error('调用微信小店发货API时发生异常:', wechatDeliveryError);
-        //   // 不阻止发货成功,只记录错误
-        // }
-
-
-
-
-
         // 发货成功后发送微信服务消息通知 - 使用新的配置化设计
         // try {
         //   const notificationResult = await sendDeliverySuccessNotification(deliveringOrder, data);

+ 239 - 28
packages/user-management-ui-mt/src/components/UserManagement.tsx

@@ -79,15 +79,7 @@ export const UserManagement = () => {
 
   const updateForm = useForm<UpdateUserFormData>({
     resolver: zodResolver(updateUserFormSchema),
-    defaultValues: {
-      username: '',
-      nickname: null,
-      email: null,
-      phone: null,
-      name: null,
-      password: '',
-      isDisabled: 0,
-    },
+    defaultValues: {}, // 初始为空,编辑时动态设置
   });
 
   const { data: usersData, isLoading, refetch } = useQuery({
@@ -212,32 +204,74 @@ export const UserManagement = () => {
   const handleEditUser = (user: UserResponse) => {
     setEditingUser(user);
     setIsCreateForm(false);
-    updateForm.reset({
-      username: user.username,
-      nickname: user.nickname,
-      email: user.email,
-      phone: user.phone,
-      name: user.name,
-      avatarFileId: user.avatarFileId,
-      isDisabled: user.isDisabled,
-    });
+
+    // 准备表单值,将null转换为undefined
+    const formValues: Partial<UpdateUserFormData> = {};
+
+    // 用户名必须设置
+    if (user.username !== undefined && user.username !== null) {
+      formValues.username = user.username;
+    }
+
+    // 其他字段:如果为null,设置为undefined(不设置)
+    if (user.nickname !== undefined && user.nickname !== null) {
+      formValues.nickname = user.nickname;
+    }
+    if (user.email !== undefined && user.email !== null) {
+      formValues.email = user.email;
+    }
+    if (user.phone !== undefined && user.phone !== null) {
+      formValues.phone = user.phone;
+    }
+    if (user.name !== undefined && user.name !== null) {
+      formValues.name = user.name;
+    }
+    if (user.avatarFileId !== undefined && user.avatarFileId !== null) {
+      formValues.avatarFileId = user.avatarFileId;
+    }
+    if (user.isDisabled !== undefined && user.isDisabled !== null) {
+      formValues.isDisabled = user.isDisabled;
+    }
+
+    console.debug('设置编辑表单值:', formValues);
+    updateForm.reset(formValues);
     setIsModalOpen(true);
   };
 
   // 处理创建表单提交
   const handleCreateSubmit = async (data: CreateUserFormData) => {
     try {
+      console.debug('开始创建用户:', data);
       const res = await userClientManager.get().index.$post({
         json: data
       });
+
+      console.debug('创建用户响应:', { status: res.status });
+
       if (res.status !== 201) {
-        throw new Error('创建用户失败');
+        // 尝试获取错误信息
+        let errorMessage = '创建用户失败';
+        try {
+          const errorData = await res.json();
+          console.debug('创建用户错误响应数据:', errorData);
+          errorMessage = errorData.message || errorMessage;
+        } catch (jsonError) {
+          console.debug('无法解析错误响应JSON:', jsonError);
+        }
+        throw new Error(errorMessage);
       }
+
+      // 获取成功响应数据
+      const result = await res.json();
+      console.debug('创建用户成功:', result);
+
       toast.success('用户创建成功');
       setIsModalOpen(false);
       refetch();
-    } catch {
-      toast.error('创建失败,请重试');
+    } catch (error) {
+      console.error('创建用户失败:', error);
+      const errorMessage = error instanceof Error ? error.message : '创建失败,请重试';
+      toast.error(`创建失败: ${errorMessage}`);
     }
   };
 
@@ -246,18 +280,178 @@ export const UserManagement = () => {
     if (!editingUser) return;
 
     try {
+      console.debug('开始更新用户,原始数据:', {
+        userId: editingUser.id,
+        data: data,
+        dataKeys: Object.keys(data),
+        passwordValue: data.password,
+        passwordType: typeof data.password,
+        passwordLength: data.password ? data.password.length : 0
+      });
+
+      // 处理提交数据:修剪字符串字段并移除空值
+      const submitData: Record<string, any> = {};
+
+      // 首先处理密码字段
+      if (data.password !== undefined && data.password !== null && data.password !== '') {
+        if (typeof data.password === 'string') {
+          const trimmedPassword = data.password.trim();
+          if (trimmedPassword !== '') {
+            if (trimmedPassword.length < 6) {
+              toast.error('密码长度至少6位');
+              return;
+            }
+            submitData.password = trimmedPassword;
+          }
+          // 如果trimmedPassword === '',不设置submitData.password字段
+        }
+      }
+      // 如果data.password是undefined/null/空字符串,不设置submitData.password字段
+
+      try {
+        // 处理其他字段
+        for (const [key, value] of Object.entries(data)) {
+          // 跳过密码字段,已经处理过了
+          if (key === 'password') continue;
+
+          if (value === undefined || value === null) {
+            // 跳过undefined和null
+            continue;
+          }
+
+          if (typeof value === 'string') {
+            const trimmedValue = value.trim();
+            if (trimmedValue === '') {
+              // 空字符串,跳过(不包含在提交数据中)
+              console.debug(`字段 ${key} 为空字符串,已跳过`);
+              continue;
+            }
+
+            // 特殊处理用户名字段
+            if (key === 'username' && trimmedValue.length < 3) {
+              // 用户名至少3个字符
+              toast.error('用户名至少3个字符');
+              return; // 直接返回,不提交
+            } else if (key === 'email') {
+              // 邮箱格式验证(简单验证)
+              const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+              if (!emailRegex.test(trimmedValue)) {
+                toast.error('邮箱格式不正确');
+                return;
+              }
+              submitData[key] = trimmedValue;
+            } else {
+              submitData[key] = trimmedValue;
+            }
+          } else {
+            // 非字符串字段
+            if (key === 'isDisabled') {
+              // 确保 isDisabled 是数字 0 或 1
+              const numValue = Number(value);
+              if (numValue === 0 || numValue === 1) {
+                submitData[key] = numValue;
+              } else {
+                console.warn(`isDisabled 值无效: ${value},应为 0 或 1`);
+                // 跳过无效值
+              }
+            } else if (key === 'avatarFileId') {
+              // avatarFileId 应该是正数或null
+              if (value !== null && value !== undefined) {
+                const numValue = Number(value);
+                if (numValue > 0) {
+                  submitData[key] = numValue;
+                } else {
+                  console.warn(`avatarFileId 值无效: ${value},应为正数`);
+                }
+              }
+            } else {
+              // 其他非字符串字段直接传递
+              submitData[key] = value;
+            }
+          }
+        }
+      } catch (error) {
+        console.error('处理提交数据时出错:', error);
+        return;
+      }
+
+      // 检查是否有实际修改的字段
+      if (Object.keys(submitData).length === 0) {
+        toast.info('没有修改任何内容');
+        setIsModalOpen(false);
+        return;
+      }
+
+
+      // 使用schema验证数据格式
+      try {
+        const validatedData = updateUserFormSchema.parse(submitData);
+        console.debug('数据验证通过:', validatedData);
+        console.debug('验证后的数据密码字段:', {
+          hasPassword: 'password' in validatedData,
+          passwordValue: validatedData.password ? `[长度:${validatedData.password.length}]` : '[未设置]'
+        });
+      } catch (validationError: any) {
+        console.error('数据验证失败:', validationError);
+        console.error('验证失败的数据:', submitData);
+
+        // 提取具体的验证错误信息
+        let errorMessage = '数据格式错误,请检查输入';
+        if (validationError?.errors?.length > 0) {
+          const errors = validationError.errors.map((err: any) =>
+            `${err.path?.join('.') || '字段'}: ${err.message}`
+          ).join('; ');
+          errorMessage = `验证错误: ${errors}`;
+          console.error('详细验证错误:', errors);
+        }
+
+        toast.error(errorMessage);
+        return;
+      }
+
+      // 直接使用表单数据,react-hook-form已经通过zodResolver验证过了
       const res = await userClientManager.get()[':id']['$put']({
         param: { id: editingUser.id },
-        json: data
+        json: submitData
       });
+
+      console.debug('更新用户响应:', { status: res.status });
+
       if (res.status !== 200) {
-        throw new Error('更新用户失败');
+        // 尝试获取错误信息
+        let errorMessage = '更新用户失败';
+        let errorDetails: any = null;
+        try {
+          const errorData = await res.json();
+          console.debug('更新用户错误响应数据:', errorData);
+          errorMessage = errorData.message || errorMessage;
+          errorDetails = errorData;
+        } catch (jsonError) {
+          console.debug('无法解析错误响应JSON:', jsonError);
+        }
+
+        // 如果是参数错误,显示更详细的信息
+        if (res.status === 400 && errorDetails?.errors) {
+          const validationErrors = errorDetails.errors.map((err: any) =>
+            `${err.path?.join('.') || '字段'}: ${err.message}`
+          ).join('; ');
+          errorMessage = `参数验证失败: ${validationErrors}`;
+        }
+
+        throw new Error(errorMessage);
       }
+
+      // 获取成功响应数据
+      const result = await res.json();
+      console.debug('更新用户成功:', result);
+
       toast.success('用户更新成功');
       setIsModalOpen(false);
       refetch();
-    } catch {
-      toast.error('更新失败,请重试');
+    } catch (error) {
+      console.error('更新用户失败:', error);
+      const errorMessage = error instanceof Error ? error.message : '更新失败,请重试';
+      toast.error(`更新失败: ${errorMessage}`);
     }
   };
 
@@ -271,16 +465,33 @@ export const UserManagement = () => {
     if (!userToDelete) return;
 
     try {
+      console.debug('开始删除用户:', { userId: userToDelete });
       const res = await userClientManager.get()[':id']['$delete']({
         param: { id: userToDelete }
       });
+
+      console.debug('删除用户响应:', { status: res.status });
+
       if (res.status !== 204) {
-        throw new Error('删除用户失败');
+        // 尝试获取错误信息
+        let errorMessage = '删除用户失败';
+        try {
+          const errorData = await res.json();
+          console.debug('删除用户错误响应数据:', errorData);
+          errorMessage = errorData.message || errorMessage;
+        } catch (jsonError) {
+          console.debug('无法解析错误响应JSON:', jsonError);
+        }
+        throw new Error(errorMessage);
       }
+
+      console.debug('删除用户成功');
       toast.success('用户删除成功');
       refetch();
-    } catch {
-      toast.error('删除失败,请重试');
+    } catch (error) {
+      console.error('删除用户失败:', error);
+      const errorMessage = error instanceof Error ? error.message : '删除失败,请重试';
+      toast.error(`删除失败: ${errorMessage}`);
     } finally {
       setDeleteDialogOpen(false);
       setUserToDelete(null);

+ 1 - 1
packages/user-module/src/schemas/user.schema.ts

@@ -123,7 +123,7 @@ export const UpdateUserDto = z.object({
   }),
   password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').optional().openapi({
     example: 'password123',
-    description: '密码,最少6位'
+    description: '密码,最少6位(留空则不修改)'
   }),
   phone: z.string().max(255, '手机号最多255个字符').nullable().optional().openapi({
     example: '13800138000',

+ 1 - 1
web/src/server/renderer.tsx

@@ -5,7 +5,7 @@ import process from 'node:process'
 // 全局配置常量
 const GLOBAL_CONFIG: GlobalConfig = {
   OSS_BASE_URL: process.env.OSS_BASE_URL || 'https://oss.d8d.fun',
-  APP_NAME: process.env.APP_NAME || '多八多Aider',
+  APP_NAME: process.env.APP_NAME || '长愿青',
 }
 
 // export const renderer = reactRenderer(({ children }) => {