Răsfoiți Sursa

✨ feat(admin): 添加收货地址管理功能

- 在菜单中添加【收货地址】导航项,使用MapPin图标
- 创建完整的收货地址管理页面,支持列表、搜索、创建、编辑、删除功能
- 集成用户选择器组件,支持按用户筛选收货地址
- 支持设置默认收货地址和地址状态管理
- 优化schema中的数字类型定义,确保类型安全
yourname 4 luni în urmă
părinte
comite
d44e189b50

+ 9 - 1
src/client/admin-shadcn/menu.tsx

@@ -16,7 +16,8 @@ import {
   Building,
   UserCheck,
   CreditCard,
-  TrendingUp
+  TrendingUp,
+  MapPin
 } from 'lucide-react';
 
 export interface MenuItem {
@@ -176,6 +177,13 @@ export const useMenu = () => {
       path: '/admin/agents',
       permission: 'agent:manage'
     },
+    {
+      key: 'delivery-addresses',
+      label: '收货地址',
+      icon: <MapPin className="h-4 w-4" />,
+      path: '/admin/delivery-addresses',
+      permission: 'user:manage'
+    },
     {
       key: 'cards',
       label: '卡券管理',

+ 688 - 0
src/client/admin-shadcn/pages/DeliveryAddresses.tsx

@@ -0,0 +1,688 @@
+import React, { useState } from 'react';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { format } from 'date-fns';
+import { zhCN } from 'date-fns/locale';
+import { toast } from 'sonner';
+import { Plus, Search, Edit, Trash2, MapPin } from 'lucide-react';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import type { InferRequestType, InferResponseType } from 'hono/client';
+
+import { deliveryAddressClient } from '@/client/api';
+import { CreateDeliveryAddressDto, UpdateDeliveryAddressDto } from '@/server/modules/delivery-address/delivery-address.schema';
+import { Button } from '@/client/components/ui/button';
+import { Input } from '@/client/components/ui/input';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/client/components/ui/table';
+import { Badge } from '@/client/components/ui/badge';
+import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog';
+import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
+import { Switch } from '@/client/components/ui/switch';
+import { Skeleton } from '@/client/components/ui/skeleton';
+import { DataTablePagination } from '@/client/admin-shadcn/components/DataTablePagination';
+import { UserSelector } from '@/client/admin-shadcn/components/UserSelector';
+
+// 类型定义
+type DeliveryAddressResponse = InferResponseType<typeof deliveryAddressClient.$get, 200>['data'][0];
+type CreateRequest = InferRequestType<typeof deliveryAddressClient.$post>['json'];
+type UpdateRequest = InferRequestType<typeof deliveryAddressClient[':id']['$put']>['json'];
+
+// 表单schema
+const createFormSchema = CreateDeliveryAddressDto;
+const updateFormSchema = UpdateDeliveryAddressDto;
+
+export const DeliveryAddressesPage = () => {
+  const queryClient = useQueryClient();
+  
+  // 状态管理
+  const [searchParams, setSearchParams] = useState({
+    page: 1,
+    limit: 10,
+    search: '',
+    userId: undefined as number | undefined,
+  });
+  const [isModalOpen, setIsModalOpen] = useState(false);
+  const [editingAddress, setEditingAddress] = useState<DeliveryAddressResponse | null>(null);
+  const [isCreateForm, setIsCreateForm] = useState(true);
+  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+  const [addressToDelete, setAddressToDelete] = useState<number | null>(null);
+
+  // 表单实例
+  const createForm = useForm<CreateRequest>({
+    resolver: zodResolver(createFormSchema),
+    defaultValues: {
+      userId: 0,
+      name: '',
+      phone: '',
+      address: '',
+      receiverProvince: 0,
+      receiverCity: 0,
+      receiverDistrict: 0,
+      receiverTown: 0,
+      isDefault: 0,
+    },
+  });
+
+  const updateForm = useForm<UpdateRequest>({
+    resolver: zodResolver(updateFormSchema),
+  });
+
+  // 数据查询
+  const { data, isLoading, refetch } = useQuery({
+    queryKey: ['delivery-addresses', searchParams],
+    queryFn: async () => {
+      const res = await deliveryAddressClient.$get({
+        query: {
+          page: searchParams.page,
+          pageSize: searchParams.limit,
+          keyword: searchParams.search,
+          ...(searchParams.userId && { userId: searchParams.userId }),
+        }
+      });
+      if (res.status !== 200) throw new Error('获取收货地址列表失败');
+      return await res.json();
+    }
+  });
+
+  // 创建地址
+  const createMutation = useMutation({
+    mutationFn: async (data: CreateRequest) => {
+      const res = await deliveryAddressClient.$post({ json: data });
+      if (res.status !== 201) throw new Error('创建失败');
+      return await res.json();
+    },
+    onSuccess: () => {
+      toast.success('收货地址创建成功');
+      setIsModalOpen(false);
+      refetch();
+      createForm.reset();
+    },
+    onError: (error) => {
+      toast.error(error.message || '创建失败');
+    }
+  });
+
+  // 更新地址
+  const updateMutation = useMutation({
+    mutationFn: async ({ id, data }: { id: number; data: UpdateRequest }) => {
+      const res = await deliveryAddressClient[':id']['$put']({
+        param: { id: id.toString() },
+        json: data,
+      });
+      if (res.status !== 200) throw new Error('更新失败');
+      return await res.json();
+    },
+    onSuccess: () => {
+      toast.success('收货地址更新成功');
+      setIsModalOpen(false);
+      refetch();
+    },
+    onError: (error) => {
+      toast.error(error.message || '更新失败');
+    }
+  });
+
+  // 删除地址
+  const deleteMutation = useMutation({
+    mutationFn: async (id: number) => {
+      const res = await deliveryAddressClient[':id']['$delete']({
+        param: { id: id.toString() },
+      });
+      if (res.status !== 204) throw new Error('删除失败');
+    },
+    onSuccess: () => {
+      toast.success('收货地址删除成功');
+      setDeleteDialogOpen(false);
+      refetch();
+    },
+    onError: (error) => {
+      toast.error(error.message || '删除失败');
+    }
+  });
+
+  // 业务逻辑函数
+  const handleSearch = () => {
+    setSearchParams(prev => ({ ...prev, page: 1 }));
+  };
+
+  const handleCreateAddress = () => {
+    setIsCreateForm(true);
+    setEditingAddress(null);
+    createForm.reset();
+    setIsModalOpen(true);
+  };
+
+  const handleEditAddress = (address: DeliveryAddressResponse) => {
+    setIsCreateForm(false);
+    setEditingAddress(address);
+    
+    updateForm.reset({
+      name: address.name,
+      phone: address.phone,
+      address: address.address,
+      receiverProvince: address.receiverProvince,
+      receiverCity: address.receiverCity,
+      receiverDistrict: address.receiverDistrict,
+      receiverTown: address.receiverTown,
+      isDefault: address.isDefault,
+    });
+    
+    setIsModalOpen(true);
+  };
+
+  const handleDeleteAddress = (id: number) => {
+    setAddressToDelete(id);
+    setDeleteDialogOpen(true);
+  };
+
+  const confirmDelete = () => {
+    if (addressToDelete) {
+      deleteMutation.mutate(addressToDelete);
+    }
+  };
+
+  const handleCreateSubmit = (data: CreateRequest) => {
+    createMutation.mutate(data);
+  };
+
+  const handleUpdateSubmit = (data: UpdateRequest) => {
+    if (editingAddress) {
+      updateMutation.mutate({ id: editingAddress.id, data });
+    }
+  };
+
+  // 状态显示
+  const getStatusBadge = (status: number) => {
+    switch (status) {
+      case 1:
+        return <Badge variant="default">正常</Badge>;
+      case 2:
+        return <Badge variant="secondary">禁用</Badge>;
+      case 3:
+        return <Badge variant="destructive">删除</Badge>;
+      default:
+        return <Badge variant="outline">未知</Badge>;
+    }
+  };
+
+  const getIsDefaultBadge = (isDefault: number) => {
+    return isDefault === 1 ? (
+      <Badge variant="default">默认</Badge>
+    ) : (
+      <Badge variant="outline">非默认</Badge>
+    );
+  };
+
+  // 加载状态
+  if (isLoading) {
+    return (
+      <div className="space-y-4">
+        <div className="flex justify-between items-center">
+          <Skeleton className="h-8 w-48" />
+          <Skeleton className="h-10 w-32" />
+        </div>
+        
+        <Card>
+          <CardContent className="pt-6">
+            <div className="space-y-3">
+              {[...Array(5)].map((_, i) => (
+                <div key={i} className="flex gap-4">
+                  <Skeleton className="h-10 flex-1" />
+                  <Skeleton className="h-10 flex-1" />
+                  <Skeleton className="h-10 flex-1" />
+                  <Skeleton className="h-10 w-20" />
+                </div>
+              ))}
+            </div>
+          </CardContent>
+        </Card>
+      </div>
+    );
+  }
+
+  return (
+    <div className="space-y-4">
+      {/* 页面标题 */}
+      <div className="flex justify-between items-center">
+        <div>
+          <h1 className="text-2xl font-bold">用户收货地址</h1>
+          <p className="text-sm text-muted-foreground">管理用户的收货地址信息</p>
+        </div>
+        <Button onClick={handleCreateAddress}>
+          <Plus className="mr-2 h-4 w-4" />
+          创建收货地址
+        </Button>
+      </div>
+
+      {/* 搜索区域 */}
+      <Card>
+        <CardHeader>
+          <CardTitle>搜索筛选</CardTitle>
+          <CardDescription>根据条件筛选收货地址</CardDescription>
+        </CardHeader>
+        <CardContent>
+          <div className="flex gap-4">
+            <div className="flex-1">
+              <div className="relative">
+                <Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
+                <Input
+                  placeholder="搜索姓名、手机号、地址..."
+                  value={searchParams.search}
+                  onChange={(e) => setSearchParams(prev => ({ ...prev, search: e.target.value }))}
+                  className="pl-8"
+                />
+              </div>
+            </div>
+            <div className="w-64">
+              <UserSelector
+                value={searchParams.userId}
+                onChange={(value) => setSearchParams(prev => ({ ...prev, userId: value }))}
+                placeholder="选择用户"
+              />
+            </div>
+            <Button onClick={handleSearch}>搜索</Button>
+          </div>
+        </CardContent>
+      </Card>
+
+      {/* 数据表格 */}
+      <Card>
+        <CardHeader>
+          <CardTitle>收货地址列表</CardTitle>
+          <CardDescription>
+            共 {data?.pagination.total || 0} 条记录
+          </CardDescription>
+        </CardHeader>
+        <CardContent>
+          <div className="rounded-md border">
+            <Table>
+              <TableHeader>
+                <TableRow>
+                  <TableHead>ID</TableHead>
+                  <TableHead>用户</TableHead>
+                  <TableHead>收货人</TableHead>
+                  <TableHead>手机号</TableHead>
+                  <TableHead>地址</TableHead>
+                  <TableHead>状态</TableHead>
+                  <TableHead>默认</TableHead>
+                  <TableHead>创建时间</TableHead>
+                  <TableHead className="text-right">操作</TableHead>
+                </TableRow>
+              </TableHeader>
+              <TableBody>
+                {data?.data.map((address) => (
+                  <TableRow key={address.id}>
+                    <TableCell>{address.id}</TableCell>
+                    <TableCell>{address.user?.username || '-'}</TableCell>
+                    <TableCell>{address.name}</TableCell>
+                    <TableCell>{address.phone}</TableCell>
+                    <TableCell className="max-w-xs truncate" title={address.address}>
+                      {address.address}
+                    </TableCell>
+                    <TableCell>{getStatusBadge(address.state)}</TableCell>
+                    <TableCell>{getIsDefaultBadge(address.isDefault)}</TableCell>
+                    <TableCell>
+                      {format(new Date(address.createdAt), 'yyyy-MM-dd HH:mm', { locale: zhCN })}
+                    </TableCell>
+                    <TableCell className="text-right">
+                      <div className="flex justify-end gap-2">
+                        <Button
+                          variant="ghost"
+                          size="icon"
+                          onClick={() => handleEditAddress(address)}
+                        >
+                          <Edit className="h-4 w-4" />
+                        </Button>
+                        <Button
+                          variant="ghost"
+                          size="icon"
+                          onClick={() => handleDeleteAddress(address.id)}
+                        >
+                          <Trash2 className="h-4 w-4" />
+                        </Button>
+                      </div>
+                    </TableCell>
+                  </TableRow>
+                ))}
+              </TableBody>
+            </Table>
+          </div>
+
+          {data?.data.length === 0 && !isLoading && (
+            <div className="text-center py-8">
+              <MapPin className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
+              <p className="text-muted-foreground">暂无收货地址数据</p>
+            </div>
+          )}
+
+          <DataTablePagination
+            currentPage={searchParams.page}
+            pageSize={searchParams.limit}
+            totalCount={data?.pagination.total || 0}
+            onPageChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
+          />
+        </CardContent>
+      </Card>
+
+      {/* 创建/编辑模态框 */}
+      <Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
+        <DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
+          <DialogHeader>
+            <DialogTitle>
+              {isCreateForm ? '创建收货地址' : '编辑收货地址'}
+            </DialogTitle>
+            <DialogDescription>
+              {isCreateForm ? '创建一个新的收货地址' : '编辑现有收货地址信息'}
+            </DialogDescription>
+          </DialogHeader>
+
+          {isCreateForm ? (
+            <Form {...createForm}>
+              <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+                <FormField
+                  control={createForm.control}
+                  name="userId"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>用户<span className="text-red-500 ml-1">*</span></FormLabel>
+                      <FormControl>
+                        <UserSelector
+                          value={field.value}
+                          onChange={field.onChange}
+                          placeholder="选择用户"
+                        />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <div className="grid grid-cols-2 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="name"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>收货人姓名<span className="text-red-500 ml-1">*</span></FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入收货人姓名" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="phone"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>手机号<span className="text-red-500 ml-1">*</span></FormLabel>
+                        <FormControl>
+                          <Input placeholder="请输入手机号" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <FormField
+                  control={createForm.control}
+                  name="address"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>详细地址<span className="text-red-500 ml-1">*</span></FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入详细地址" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <div className="grid grid-cols-4 gap-4">
+                  <FormField
+                    control={createForm.control}
+                    name="receiverProvince"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>省份ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="省份ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="receiverCity"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>城市ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="城市ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="receiverDistrict"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>区县ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="区县ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={createForm.control}
+                    name="receiverTown"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>街道ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="街道ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <FormField
+                  control={createForm.control}
+                  name="isDefault"
+                  render={({ field }) => (
+                    <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
+                      <div className="space-y-0.5">
+                        <FormLabel className="text-base">设为默认地址</FormLabel>
+                        <FormDescription>将此地址设为用户的默认收货地址</FormDescription>
+                      </div>
+                      <FormControl>
+                        <Switch
+                          checked={field.value === 1}
+                          onCheckedChange={(checked) => field.onChange(checked ? 1 : 0)}
+                        />
+                      </FormControl>
+                    </FormItem>
+                  )}
+                />
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit" disabled={createMutation.isPending}>
+                    创建
+                  </Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          ) : (
+            <Form {...updateForm}>
+              <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
+                <FormField
+                  control={updateForm.control}
+                  name="name"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>收货人姓名<span className="text-red-500 ml-1">*</span></FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入收货人姓名" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="phone"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>手机号<span className="text-red-500 ml-1">*</span></FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入手机号" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <FormField
+                  control={updateForm.control}
+                  name="address"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>详细地址<span className="text-red-500 ml-1">*</span></FormLabel>
+                      <FormControl>
+                        <Input placeholder="请输入详细地址" {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+
+                <div className="grid grid-cols-4 gap-4">
+                  <FormField
+                    control={updateForm.control}
+                    name="receiverProvince"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>省份ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="省份ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="receiverCity"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>城市ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="城市ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="receiverDistrict"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>区县ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="区县ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={updateForm.control}
+                    name="receiverTown"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>街道ID</FormLabel>
+                        <FormControl>
+                          <Input type="number" placeholder="街道ID" {...field} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+
+                <FormField
+                  control={updateForm.control}
+                  name="isDefault"
+                  render={({ field }) => (
+                    <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
+                      <div className="space-y-0.5">
+                        <FormLabel className="text-base">设为默认地址</FormLabel>
+                        <FormDescription>将此地址设为用户的默认收货地址</FormDescription>
+                      </div>
+                      <FormControl>
+                        <Switch
+                          checked={field.value === 1}
+                          onCheckedChange={(checked) => field.onChange(checked ? 1 : 0)}
+                        />
+                      </FormControl>
+                    </FormItem>
+                  )}
+                />
+
+                <DialogFooter>
+                  <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+                    取消
+                  </Button>
+                  <Button type="submit" disabled={updateMutation.isPending}>
+                    更新
+                  </Button>
+                </DialogFooter>
+              </form>
+            </Form>
+          )}
+        </DialogContent>
+      </Dialog>
+
+      {/* 删除确认对话框 */}
+      <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
+        <DialogContent>
+          <DialogHeader>
+            <DialogTitle>确认删除</DialogTitle>
+            <DialogDescription>
+              确定要删除这个收货地址吗?此操作无法撤销。
+            </DialogDescription>
+          </DialogHeader>
+          <DialogFooter>
+            <Button variant="outline" onClick={() => setDeleteDialogOpen(false)}>
+              取消
+            </Button>
+            <Button variant="destructive" onClick={confirmDelete} disabled={deleteMutation.isPending}>
+              删除
+            </Button>
+          </DialogFooter>
+        </DialogContent>
+      </Dialog>
+    </div>
+  );
+};

+ 6 - 0
src/client/admin-shadcn/routes.tsx

@@ -18,6 +18,7 @@ import { MerchantsPage } from './pages/Merchants'
 import { AgentsPage } from './pages/Agents';
 import { UserCardsPage } from './pages/UserCards';
 import { UserCardBalanceRecordsPage } from './pages/UserCardBalanceRecords';
+import { DeliveryAddressesPage } from './pages/DeliveryAddresses';
 
 export const router = createBrowserRouter([
   {
@@ -105,6 +106,11 @@ export const router = createBrowserRouter([
         element: <UserCardBalanceRecordsPage />,
         errorElement: <ErrorPage />
       },
+      {
+        path: 'delivery-addresses',
+        element: <DeliveryAddressesPage />,
+        errorElement: <ErrorPage />
+      },
       {
         path: '*',
         element: <NotFoundPage />,

+ 17 - 17
src/server/modules/delivery-address/delivery-address.schema.ts

@@ -49,7 +49,7 @@ export const DeliveryAddressSchema = z.object({
       description: '详细地址',
       example: '北京市朝阳区建国门外大街1号'
     }),
-  receiverProvince: z.coerce.number()
+  receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
     .positive('省份ID必须是正整数')
     .default(0)
@@ -57,7 +57,7 @@ export const DeliveryAddressSchema = z.object({
       description: '收货省份ID',
       example: 110000
     }),
-  receiverCity: z.coerce.number()
+  receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
     .positive('城市ID必须是正整数')
     .default(0)
@@ -65,7 +65,7 @@ export const DeliveryAddressSchema = z.object({
       description: '收货城市ID',
       example: 110100
     }),
-  receiverDistrict: z.coerce.number()
+  receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
     .positive('区县ID必须是正整数')
     .default(0)
@@ -73,7 +73,7 @@ export const DeliveryAddressSchema = z.object({
       description: '收货区县ID',
       example: 110105
     }),
-  receiverTown: z.coerce.number()
+  receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
     .positive('街道ID必须是正整数')
     .default(0)
@@ -81,7 +81,7 @@ export const DeliveryAddressSchema = z.object({
       description: '收货街道ID',
       example: 110105001
     }),
-  state: z.coerce.number()
+  state: z.coerce.number<number>()
     .int('状态必须是整数')
     .min(1, '状态最小值为1')
     .max(3, '状态最大值为3')
@@ -90,7 +90,7 @@ export const DeliveryAddressSchema = z.object({
       description: '状态:1正常,2禁用,3删除',
       example: 1
     }),
-  isDefault: z.coerce.number()
+  isDefault: z.coerce.number<number>()
     .int('是否默认必须是整数')
     .min(0, '最小值为0')
     .max(1, '最大值为1')
@@ -159,7 +159,7 @@ export const CreateDeliveryAddressDto = z.object({
       description: '详细地址',
       example: '北京市朝阳区建国门外大街1号'
     }),
-  receiverProvince: z.coerce.number()
+  receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
     .positive('省份ID必须是正整数')
     .default(0)
@@ -167,7 +167,7 @@ export const CreateDeliveryAddressDto = z.object({
       description: '收货省份ID',
       example: 110000
     }),
-  receiverCity: z.coerce.number()
+  receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
     .positive('城市ID必须是正整数')
     .default(0)
@@ -175,7 +175,7 @@ export const CreateDeliveryAddressDto = z.object({
       description: '收货城市ID',
       example: 110100
     }),
-  receiverDistrict: z.coerce.number()
+  receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
     .positive('区县ID必须是正整数')
     .default(0)
@@ -183,7 +183,7 @@ export const CreateDeliveryAddressDto = z.object({
       description: '收货区县ID',
       example: 110105
     }),
-  receiverTown: z.coerce.number()
+  receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
     .positive('街道ID必须是正整数')
     .default(0)
@@ -191,7 +191,7 @@ export const CreateDeliveryAddressDto = z.object({
       description: '收货街道ID',
       example: 110105001
     }),
-  isDefault: z.coerce.number()
+  isDefault: z.coerce.number<number>()
     .int('是否默认必须是整数')
     .min(0, '最小值为0')
     .max(1, '最大值为1')
@@ -227,7 +227,7 @@ export const UpdateDeliveryAddressDto = z.object({
       description: '详细地址',
       example: '北京市朝阳区建国门外大街1号'
     }),
-  receiverProvince: z.coerce.number()
+  receiverProvince: z.coerce.number<number>()
     .int('省份ID必须是整数')
     .positive('省份ID必须是正整数')
     .optional()
@@ -235,7 +235,7 @@ export const UpdateDeliveryAddressDto = z.object({
       description: '收货省份ID',
       example: 110000
     }),
-  receiverCity: z.coerce.number()
+  receiverCity: z.coerce.number<number>()
     .int('城市ID必须是整数')
     .positive('城市ID必须是正整数')
     .optional()
@@ -243,7 +243,7 @@ export const UpdateDeliveryAddressDto = z.object({
       description: '收货城市ID',
       example: 110100
     }),
-  receiverDistrict: z.coerce.number()
+  receiverDistrict: z.coerce.number<number>()
     .int('区县ID必须是整数')
     .positive('区县ID必须是正整数')
     .optional()
@@ -251,7 +251,7 @@ export const UpdateDeliveryAddressDto = z.object({
       description: '收货区县ID',
       example: 110105
     }),
-  receiverTown: z.coerce.number()
+  receiverTown: z.coerce.number<number>()
     .int('街道ID必须是整数')
     .positive('街道ID必须是正整数')
     .optional()
@@ -259,7 +259,7 @@ export const UpdateDeliveryAddressDto = z.object({
       description: '收货街道ID',
       example: 110105001
     }),
-  state: z.coerce.number()
+  state: z.coerce.number<number>()
     .int('状态必须是整数')
     .min(1, '状态最小值为1')
     .max(3, '状态最大值为3')
@@ -268,7 +268,7 @@ export const UpdateDeliveryAddressDto = z.object({
       description: '状态:1正常,2禁用,3删除',
       example: 1
     }),
-  isDefault: z.coerce.number()
+  isDefault: z.coerce.number<number>()
     .int('是否默认必须是整数')
     .min(0, '最小值为0')
     .max(1, '最大值为1')