Переглянути джерело

🐛 fix(delivery-address): 修复四级地址字段schema验证规则矛盾

- 修复四级地址字段schema验证规则矛盾:将`.positive('必须是正整数')`改为`.min(0, '不能为负数')`
- 修复所有相关schema文件:admin-delivery-address.mt.schema.ts、delivery-address.mt.schema.ts、user-delivery-address.mt.schema.ts
- 修复收货地址管理UI组件:使用管理员schema、修复数据访问、添加表单调试信息
- 修复集成测试:修正mock数据结构、修复组件mock路径、修复API调用检查
- 导出管理员schema到index.ts

🤖 Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 1 місяць тому
батько
коміт
da2ced7b67

+ 3 - 1
CLAUDE.md

@@ -17,4 +17,6 @@
 - 前端是 hono/client  hc  rpc 的,不是直接fetch
 - bmad-core dir is in .bmad-core
 - 必须用中文回答
-- **git提交**: 当遇到git锁文件冲突时,使用单条命令:`rm -f /mnt/code/184-172-template-6/.git/index.lock && git add <文件> && git commit -m "提交信息"`
+- **git提交**: 当遇到git锁文件冲突时,使用单条命令:`rm -f /mnt/code/184-172-template-6/.git/index.lock && git add <文件> && git commit -m "提交信息"`
+- **测试调试**: 使用 `pnpm test -t "测试名称"` 来运行特定测试查看详细信息
+- **表单调试**: 表单提交失败时,在表单form onsubmit=form.handleSubmit的第二个参数中加console.debug来看表单验证错误,例如:`form.handleSubmit(handleSubmit, (errors) => console.debug('表单验证错误:', errors))`

+ 7 - 6
packages/delivery-address-management-ui-mt/src/components/DeliveryAddressManagement.tsx

@@ -8,7 +8,7 @@ import { useForm } from 'react-hook-form';
 import { zodResolver } from '@hookform/resolvers/zod';
 
 import { deliveryAddressClient, deliveryAddressClientManager } from '../api/deliveryAddressClient';
-import { CreateDeliveryAddressDto, UpdateDeliveryAddressDto } from '@d8d/delivery-address-module-mt/schemas';
+import { CreateAdminDeliveryAddressDto, UpdateAdminDeliveryAddressDto } from '@d8d/delivery-address-module-mt/schemas';
 import { Button } from '@d8d/shared-ui-components/components/ui/button';
 import { Input } from '@d8d/shared-ui-components/components/ui/input';
 import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@d8d/shared-ui-components/components/ui/card';
@@ -27,8 +27,8 @@ import { DeliveryAddressState, DefaultAddressState } from '../types/delivery-add
 import type { InferResponseType } from 'hono/client';
 type DeliveryAddress = InferResponseType<typeof deliveryAddressClient.index.$get, 200>['data'][0];
 // 表单schema
-const createFormSchema = CreateDeliveryAddressDto;
-const updateFormSchema = UpdateDeliveryAddressDto;
+const createFormSchema = CreateAdminDeliveryAddressDto;
+const updateFormSchema = UpdateAdminDeliveryAddressDto;
 
 export const DeliveryAddressManagement: React.FC = () => {
   // const queryClient = useQueryClient(); // 暂时注释掉未使用的变量
@@ -181,6 +181,7 @@ export const DeliveryAddressManagement: React.FC = () => {
   };
 
   const handleCreateSubmit = (data: any) => {
+    console.debug('创建表单提交数据:', data);
     createMutation.mutate(data);
   };
 
@@ -251,7 +252,7 @@ export const DeliveryAddressManagement: React.FC = () => {
       <CardHeader>
         <CardTitle>收货地址列表</CardTitle>
         <CardDescription>
-          共 {data?.pagination.total || 0} 条收货地址记录
+          共 {(data?.pagination?.total) || 0} 条收货地址记录
         </CardDescription>
       </CardHeader>
       <CardContent>
@@ -269,7 +270,7 @@ export const DeliveryAddressManagement: React.FC = () => {
             </TableRow>
           </TableHeader>
           <TableBody>
-            {data?.data?.map((address) => (
+            {(data?.data || []).map((address) => (
               <TableRow key={address.id} data-testid={`address-row-${address.id}`}>
                 <TableCell data-testid={`address-user-${address.id}`}>
                   <div className="flex items-center gap-2">
@@ -390,7 +391,7 @@ export const DeliveryAddressManagement: React.FC = () => {
 
           {isCreateForm ? (
             <Form {...createForm}>
-              <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+              <form onSubmit={createForm.handleSubmit(handleCreateSubmit, (errors) => console.debug('创建表单验证错误:', errors))} className="space-y-4">
                 <FormField
                   control={createForm.control}
                   name="userId"

+ 101 - 90
packages/delivery-address-management-ui-mt/tests/integration/delivery-address-management.integration.test.tsx

@@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
 import { DeliveryAddressManagement } from '../../src/components/DeliveryAddressManagement';
 import { deliveryAddressClient, deliveryAddressClientManager } from '../../src/api/deliveryAddressClient';
 
+
 // 完整的mock响应对象
 const createMockResponse = (status: number, data?: any) => ({
   status,
@@ -26,11 +27,16 @@ const createMockResponse = (status: number, data?: any) => ({
 // Mock API client
 vi.mock('../../src/api/deliveryAddressClient', () => {
   const mockDeliveryAddressClient = {
-    $get: vi.fn(() => Promise.resolve({ status: 200, body: null })),
-    $post: vi.fn(() => Promise.resolve({ status: 201, body: null })),
+    index: {
+      $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
+        data: [],
+        pagination: { total: 0, current: 1, pageSize: 10 }
+      }))),
+      $post: vi.fn(() => Promise.resolve(createMockResponse(201, { id: 1, name: '默认地址' }))),
+    },
     ':id': {
-      $put: vi.fn(() => Promise.resolve({ status: 200, body: null })),
-      $delete: vi.fn(() => Promise.resolve({ status: 204, body: null })),
+      $put: vi.fn(() => Promise.resolve(createMockResponse(200))),
+      $delete: vi.fn(() => Promise.resolve(createMockResponse(204))),
     },
   };
 
@@ -52,8 +58,8 @@ vi.mock('sonner', () => ({
   },
 }));
 
-// Mock UserSelector
-vi.mock('@d8d/user-management-ui/components', () => ({
+// Mock UserSelector (多租户版本)
+vi.mock('@d8d/user-management-ui-mt/components', () => ({
   UserSelector: ({ value, onChange, placeholder, 'data-testid': testId }: any) => (
     <select
       data-testid={testId || 'user-selector'}
@@ -67,8 +73,8 @@ vi.mock('@d8d/user-management-ui/components', () => ({
   ),
 }));
 
-// Mock AreaSelect4Level
-vi.mock('@d8d/area-management-ui/components', () => ({
+// Mock AreaSelect4Level (多租户版本)
+vi.mock('@d8d/area-management-ui-mt/components', () => ({
   AreaSelect4Level: ({
     provinceValue,
     cityValue,
@@ -142,57 +148,57 @@ describe('地址管理集成测试', () => {
 
   it('应该完成完整的地址CRUD流程', async () => {
     const mockAddresses = {
-      data: {
-        list: [
-          {
+      data: [
+        {
+          id: 1,
+          userId: 1,
+          name: '张三',
+          phone: '13800138000',
+          address: '朝阳区三里屯街道',
+          receiverProvince: 1,
+          receiverCity: 2,
+          receiverDistrict: 3,
+          receiverTown: 4,
+          state: 1,
+          isDefault: 1,
+          createdBy: 1,
+          updatedBy: 1,
+          createdAt: '2024-01-01T00:00:00Z',
+          updatedAt: '2024-01-01T00:00:00Z',
+          user: {
             id: 1,
-            userId: 1,
-            name: '张三',
+            name: 'user1',
             phone: '13800138000',
-            address: '朝阳区三里屯街道',
-            receiverProvince: 1,
-            receiverCity: 2,
-            receiverDistrict: 3,
-            receiverTown: 4,
-            state: 1,
-            isDefault: 1,
-            createdBy: 1,
-            updatedBy: 1,
-            createdAt: '2024-01-01T00:00:00Z',
-            updatedAt: '2024-01-01T00:00:00Z',
-            user: {
-              id: 1,
-              name: 'user1',
-              phone: '13800138000',
-            },
-            province: {
-              id: 1,
-              name: '北京市',
-            },
-            city: {
-              id: 2,
-              name: '北京市',
-            },
-            district: {
-              id: 3,
-              name: '朝阳区',
-            },
-            town: {
-              id: 4,
-              name: '三里屯街道',
-            },
           },
-        ],
+          province: {
+            id: 1,
+            name: '北京市',
+          },
+          city: {
+            id: 2,
+            name: '北京市',
+          },
+          district: {
+            id: 3,
+            name: '朝阳区',
+          },
+          town: {
+            id: 4,
+            name: '三里屯街道',
+          },
+        },
+      ],
+      pagination: {
         total: 1,
-        page: 1,
-        limit: 10,
+        current: 1,
+        pageSize: 10,
       },
     };
 
     const { toast } = await import('sonner');
 
     // Mock initial address list
-    (deliveryAddressClient.$get as any).mockResolvedValue(createMockResponse(200, mockAddresses));
+    (deliveryAddressClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockAddresses));
 
     renderWithProviders(<DeliveryAddressManagement />);
 
@@ -232,13 +238,13 @@ describe('地址管理集成测试', () => {
     fireEvent.change(townSelect, { target: { value: '4' } });
 
     // Mock successful creation
-    (deliveryAddressClient.$post as any).mockResolvedValue(createMockResponse(201, { id: 2, name: '李四' }));
+    (deliveryAddressClient.index.$post as any).mockResolvedValue(createMockResponse(201, { id: 2, name: '李四' }));
 
     const submitButton = screen.getByText('创建');
     fireEvent.click(submitButton);
 
     await waitFor(() => {
-      expect(deliveryAddressClient.$post).toHaveBeenCalledWith({
+      expect(deliveryAddressClientManager.get().index.$post).toHaveBeenCalledWith({
         json: {
           userId: 1,
           name: '李四',
@@ -274,7 +280,7 @@ describe('地址管理集成测试', () => {
     fireEvent.click(updateButton);
 
     await waitFor(() => {
-      expect(deliveryAddressClient[':id']['$put']).toHaveBeenCalledWith({
+      expect(deliveryAddressClientManager.get()[':id']['$put']).toHaveBeenCalledWith({
         param: { id: 1 },
         json: {
           name: '王五',
@@ -298,15 +304,13 @@ describe('地址管理集成测试', () => {
     expect(screen.getByText('确认删除')).toBeInTheDocument();
 
     // Mock successful deletion
-    (deliveryAddressClient[':id']['$delete'] as any).mockResolvedValue({
-      status: 204,
-    });
+    (deliveryAddressClient[':id']['$delete'] as any).mockResolvedValue(createMockResponse(204));
 
     const confirmDeleteButton = screen.getByText('删除');
     fireEvent.click(confirmDeleteButton);
 
     await waitFor(() => {
-      expect(deliveryAddressClient[':id']['$delete']).toHaveBeenCalledWith({
+      expect(deliveryAddressClientManager.get()[':id']['$delete']).toHaveBeenCalledWith({
         param: { id: 1 },
       });
       expect(toast.success).toHaveBeenCalledWith('收货地址删除成功');
@@ -317,7 +321,7 @@ describe('地址管理集成测试', () => {
     const { toast } = await import('sonner');
 
     // Mock API error
-    (deliveryAddressClient.$get as any).mockRejectedValue(new Error('API Error'));
+    (deliveryAddressClient.index.$get as any).mockRejectedValue(new Error('API Error'));
 
     renderWithProviders(<DeliveryAddressManagement />);
 
@@ -330,14 +334,21 @@ describe('地址管理集成测试', () => {
     const createButton = screen.getByText('创建收货地址');
     fireEvent.click(createButton);
 
+    // Fill all required fields
+    const userSelector = screen.getByTestId('create-user-selector');
     const nameInput = screen.getByPlaceholderText('请输入收货人姓名');
     const phoneInput = screen.getByPlaceholderText('请输入手机号');
+    const addressInput = screen.getByPlaceholderText('请输入详细地址');
 
+    fireEvent.change(userSelector, { target: { value: '1' } });
     fireEvent.change(nameInput, { target: { value: '测试用户' } });
     fireEvent.change(phoneInput, { target: { value: '13800138000' } });
+    fireEvent.change(addressInput, { target: { value: '详细地址信息' } });
+
+    // Area values are set with default 0 values which should pass validation
 
     // Mock creation error
-    (deliveryAddressClient.$post as any).mockRejectedValue(new Error('Creation failed'));
+    (deliveryAddressClient.index.$post as any).mockRejectedValue(new Error('Creation failed'));
 
     const submitButton = screen.getByText('创建');
     fireEvent.click(submitButton);
@@ -349,15 +360,15 @@ describe('地址管理集成测试', () => {
 
   it('应该处理搜索和过滤器集成', async () => {
     const mockAddresses = {
-      data: {
-        list: [],
+      data: [],
+      pagination: {
         total: 0,
-        page: 1,
-        limit: 10,
+        current: 1,
+        pageSize: 10,
       },
     };
 
-    (deliveryAddressClient.$get as any).mockResolvedValue(createMockResponse(200, mockAddresses));
+    (deliveryAddressClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockAddresses));
 
     renderWithProviders(<DeliveryAddressManagement />);
 
@@ -366,7 +377,7 @@ describe('地址管理集成测试', () => {
     fireEvent.change(searchInput, { target: { value: '张三' } });
 
     await waitFor(() => {
-      expect(deliveryAddressClientManager.get().$get).toHaveBeenCalledWith({
+      expect(deliveryAddressClientManager.get().index.$get).toHaveBeenCalledWith({
         query: {
           page: 1,
           pageSize: 10,
@@ -383,7 +394,7 @@ describe('地址管理集成测试', () => {
     fireEvent.click(searchButton);
 
     await waitFor(() => {
-      expect(deliveryAddressClientManager.get().$get).toHaveBeenCalledWith({
+      expect(deliveryAddressClientManager.get().index.$get).toHaveBeenCalledWith({
         query: {
           page: 1,
           pageSize: 10,
@@ -396,38 +407,38 @@ describe('地址管理集成测试', () => {
 
   it('应该正确显示地址状态和默认地址标记', async () => {
     const mockAddresses = {
-      data: {
-        list: [
-          {
+      data: [
+        {
+          id: 1,
+          userId: 1,
+          name: '张三',
+          phone: '13800138000',
+          address: '朝阳区三里屯街道',
+          receiverProvince: 1,
+          receiverCity: 2,
+          receiverDistrict: 3,
+          receiverTown: 4,
+          state: 1,
+          isDefault: 1,
+          createdBy: 1,
+          updatedBy: 1,
+          createdAt: '2024-01-01T00:00:00Z',
+          updatedAt: '2024-01-01T00:00:00Z',
+          user: {
             id: 1,
-            userId: 1,
-            name: '张三',
+            name: 'user1',
             phone: '13800138000',
-            address: '朝阳区三里屯街道',
-            receiverProvince: 1,
-            receiverCity: 2,
-            receiverDistrict: 3,
-            receiverTown: 4,
-            state: 1,
-            isDefault: 1,
-            createdBy: 1,
-            updatedBy: 1,
-            createdAt: '2024-01-01T00:00:00Z',
-            updatedAt: '2024-01-01T00:00:00Z',
-            user: {
-              id: 1,
-              name: 'user1',
-              phone: '13800138000',
-            },
           },
-        ],
+        },
+      ],
+      pagination: {
         total: 1,
-        page: 1,
-        limit: 10,
+        current: 1,
+        pageSize: 10,
       },
     };
 
-    (deliveryAddressClient.$get as any).mockResolvedValue(createMockResponse(200, mockAddresses));
+    (deliveryAddressClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockAddresses));
 
     renderWithProviders(<DeliveryAddressManagement />);
 

+ 8 - 8
packages/delivery-address-module-mt/src/schemas/admin-delivery-address.mt.schema.ts

@@ -59,7 +59,7 @@ export const AdminDeliveryAddressSchema = z.object({
     }),
   receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
-    .positive('省份ID必须是正整数')
+    .min(0, '省份ID不能为负数')
     .default(0)
     .openapi({
       description: '收货省份ID',
@@ -67,7 +67,7 @@ export const AdminDeliveryAddressSchema = z.object({
     }),
   receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
-    .positive('城市ID必须是正整数')
+    .min(0, '城市ID不能为负数')
     .default(0)
     .openapi({
       description: '收货城市ID',
@@ -75,7 +75,7 @@ export const AdminDeliveryAddressSchema = z.object({
     }),
   receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
-    .positive('区县ID必须是正整数')
+    .min(0, '区县ID不能为负数')
     .default(0)
     .openapi({
       description: '收货区县ID',
@@ -83,7 +83,7 @@ export const AdminDeliveryAddressSchema = z.object({
     }),
   receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
-    .positive('街道ID必须是正整数')
+    .min(0, '街道ID不能为负数')
     .default(0)
     .openapi({
       description: '收货街道ID',
@@ -181,7 +181,7 @@ export const CreateAdminDeliveryAddressDto = z.object({
     }),
   receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
-    .positive('省份ID必须是正整数')
+    .min(0, '省份ID不能为负数')
     .default(0)
     .openapi({
       description: '收货省份ID',
@@ -189,7 +189,7 @@ export const CreateAdminDeliveryAddressDto = z.object({
     }),
   receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
-    .positive('城市ID必须是正整数')
+    .min(0, '城市ID不能为负数')
     .default(0)
     .openapi({
       description: '收货城市ID',
@@ -197,7 +197,7 @@ export const CreateAdminDeliveryAddressDto = z.object({
     }),
   receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
-    .positive('区县ID必须是正整数')
+    .min(0, '区县ID不能为负数')
     .default(0)
     .openapi({
       description: '收货区县ID',
@@ -205,7 +205,7 @@ export const CreateAdminDeliveryAddressDto = z.object({
     }),
   receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
-    .positive('街道ID必须是正整数')
+    .min(0, '街道ID不能为负数')
     .default(0)
     .openapi({
       description: '收货街道ID',

+ 8 - 8
packages/delivery-address-module-mt/src/schemas/delivery-address.mt.schema.ts

@@ -59,7 +59,7 @@ export const DeliveryAddressSchema = z.object({
     }),
   receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
-    .positive('省份ID必须是正整数')
+    .min(0, '省份ID不能为负数')
     .default(0)
     .openapi({
       description: '收货省份ID',
@@ -67,7 +67,7 @@ export const DeliveryAddressSchema = z.object({
     }),
   receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
-    .positive('城市ID必须是正整数')
+    .min(0, '城市ID不能为负数')
     .default(0)
     .openapi({
       description: '收货城市ID',
@@ -75,7 +75,7 @@ export const DeliveryAddressSchema = z.object({
     }),
   receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
-    .positive('区县ID必须是正整数')
+    .min(0, '区县ID不能为负数')
     .default(0)
     .openapi({
       description: '收货区县ID',
@@ -83,7 +83,7 @@ export const DeliveryAddressSchema = z.object({
     }),
   receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
-    .positive('街道ID必须是正整数')
+    .min(0, '街道ID不能为负数')
     .default(0)
     .openapi({
       description: '收货街道ID',
@@ -188,7 +188,7 @@ export const CreateDeliveryAddressDto = z.object({
     }),
   receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
-    .positive('省份ID必须是正整数')
+    .min(0, '省份ID不能为负数')
     .default(0)
     .openapi({
       description: '收货省份ID',
@@ -196,7 +196,7 @@ export const CreateDeliveryAddressDto = z.object({
     }),
   receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
-    .positive('城市ID必须是正整数')
+    .min(0, '城市ID不能为负数')
     .default(0)
     .openapi({
       description: '收货城市ID',
@@ -204,7 +204,7 @@ export const CreateDeliveryAddressDto = z.object({
     }),
   receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
-    .positive('区县ID必须是正整数')
+    .min(0, '区县ID不能为负数')
     .default(0)
     .openapi({
       description: '收货区县ID',
@@ -212,7 +212,7 @@ export const CreateDeliveryAddressDto = z.object({
     }),
   receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
-    .positive('街道ID必须是正整数')
+    .min(0, '街道ID不能为负数')
     .default(0)
     .openapi({
       description: '收货街道ID',

+ 7 - 1
packages/delivery-address-module-mt/src/schemas/index.ts

@@ -4,4 +4,10 @@ export {
   UpdateDeliveryAddressDto,
   DeliveryAddressStatusEnum,
   IsDefaultEnum
-} from './delivery-address.mt.schema';
+} from './delivery-address.mt.schema';
+
+export {
+  AdminDeliveryAddressSchema,
+  CreateAdminDeliveryAddressDto,
+  UpdateAdminDeliveryAddressDto
+} from './admin-delivery-address.mt.schema';

+ 8 - 8
packages/delivery-address-module-mt/src/schemas/user-delivery-address.mt.schema.ts

@@ -60,7 +60,7 @@ export const UserDeliveryAddressSchema = z.object({
     }),
   receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
-    .positive('省份ID必须是正整数')
+    .min(0, '省份ID不能为负数')
     .default(0)
     .openapi({
       description: '收货省份ID',
@@ -68,7 +68,7 @@ export const UserDeliveryAddressSchema = z.object({
     }),
   receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
-    .positive('城市ID必须是正整数')
+    .min(0, '城市ID不能为负数')
     .default(0)
     .openapi({
       description: '收货城市ID',
@@ -76,7 +76,7 @@ export const UserDeliveryAddressSchema = z.object({
     }),
   receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
-    .positive('区县ID必须是正整数')
+    .min(0, '区县ID不能为负数')
     .default(0)
     .openapi({
       description: '收货区县ID',
@@ -84,7 +84,7 @@ export const UserDeliveryAddressSchema = z.object({
     }),
   receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
-    .positive('街道ID必须是正整数')
+    .min(0, '街道ID不能为负数')
     .default(0)
     .openapi({
       description: '收货街道ID',
@@ -176,7 +176,7 @@ export const CreateUserDeliveryAddressDto = z.object({
     }),
   receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
-    .positive('省份ID必须是正整数')
+    .min(0, '省份ID不能为负数')
     .default(0)
     .openapi({
       description: '收货省份ID',
@@ -184,7 +184,7 @@ export const CreateUserDeliveryAddressDto = z.object({
     }),
   receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
-    .positive('城市ID必须是正整数')
+    .min(0, '城市ID不能为负数')
     .default(0)
     .openapi({
       description: '收货城市ID',
@@ -192,7 +192,7 @@ export const CreateUserDeliveryAddressDto = z.object({
     }),
   receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
-    .positive('区县ID必须是正整数')
+    .min(0, '区县ID不能为负数')
     .default(0)
     .openapi({
       description: '收货区县ID',
@@ -200,7 +200,7 @@ export const CreateUserDeliveryAddressDto = z.object({
     }),
   receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
-    .positive('街道ID必须是正整数')
+    .min(0, '街道ID不能为负数')
     .default(0)
     .openapi({
       description: '收货街道ID',