husenlin пре 1 месец
родитељ
комит
80d6c08304

+ 6 - 0
backend/.dockerignore

@@ -20,3 +20,9 @@ build
 Dockerfile
 docker-compose*.yml
 README*
+logs/
+docs/
+.idea/
+.vscode/
+tests/
+sql/

+ 2 - 2
backend/Dockerfile

@@ -10,8 +10,8 @@ WORKDIR /app
 COPY requirements.txt .
 
 RUN python -m venv /opt/venv \
-    && /opt/venv/bin/pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple \
-    && /opt/venv/bin/pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
+    && /opt/venv/bin/pip install --upgrade pip -i https://pypi.doubanio.com/simple \
+    && /opt/venv/bin/pip install -r requirements.txt -i https://pypi.doubanio.com/simple
 
 
 FROM python:3.13-slim AS runtime

BIN
backend/requirements.txt


+ 38 - 0
frontend/.dockerignore

@@ -0,0 +1,38 @@
+# --- 依赖包 (最核心的修复) ---
+node_modules
+npm-debug.log
+yarn-error.log
+pnpm-debug.log
+
+# --- 构建产物 (避免把旧的打包文件放进去) ---
+dist
+build
+.next
+out
+*.tsbuildinfo
+
+# --- 版本控制 (容器内不需要 .git 历史) ---
+.git
+.gitignore
+.svn
+
+# --- 编辑器与系统文件 (保持镜像整洁) ---
+.DS_Store
+Thumbs.db
+*.swp
+*.swo
+*~
+.idea
+.vscode
+
+# --- 环境变量与安全 (防止密钥泄露) ---
+.env
+.env.local
+.env.*.local
+*.pem
+*.key
+
+# --- Docker 相关文件 (避免递归复制) ---
+Dockerfile
+docker-compose.yml
+.dockerignore

+ 5 - 4
frontend/Dockerfile

@@ -1,8 +1,8 @@
-FROM node:24 AS build
+FROM node:24-alpine AS builder
 
 WORKDIR /app
 
-COPY package.json pnpm-lock.yaml ./
+COPY package.json ./
 
 RUN npm install -g pnpm && pnpm install
 
@@ -10,9 +10,10 @@ COPY . .
 
 RUN pnpm build
 
-FROM nginx:1.29.8 AS runtime
 
-COPY --from=build /app/dist /usr/share/nginx/html
+FROM nginx:stable-alpine
+
+COPY --from=builder /app/dist /usr/share/nginx/html
 
 COPY nginx.conf /etc/nginx/conf.d/default.conf
 

+ 1 - 1
frontend/package.json

@@ -9,7 +9,7 @@
     "dev": "vite",
     "dev:force": "vite --force",
     "prod": "vite --mode prod",
-    "build": "vue-tsc --noEmit & vite build",
+    "build": "vite build",
     "build:pro": "pnpm vite build --mode pro",
     "build:gitee": "pnpm vite build --mode gitee",
     "build:dev": "pnpm vite build --mode dev",

+ 43 - 48
frontend/src/api/module_payment/enterprise.ts

@@ -1,51 +1,50 @@
-import type { EnterpriseCreateSchema, EnterpriseListOutSchema, EnterpriseOutSchema, EnterpriseUpdateSchema } from './schema';
-import request from '@/utils/request';
+import request from "@/utils/request";
 
 // 身份类型选项
 export const IDENTITY_TYPE_OPTIONS = [
-  { label: '支付宝用户ID', value: 'ALIPAY_USER_ID' },
+  { label: "支付宝用户ID", value: "ALIPAY_USER_ID" },
   // { label: '支付宝登录号', value: 'ALIPAY_LOGON_ID' },
-  { label: '企业邮箱', value: 'ENTERPRISE_EMAIL' }
+  { label: "企业邮箱", value: "ENTERPRISE_EMAIL" },
 ];
 
 // 注册模式选项
 export const REGISTER_MODE_OPTIONS = [
-  { label: '正常注册', value: 'NORMAL' },
-  { label: 'ISV代理', value: 'ISV_AGENT' },
-  { label: 'ISV虚拟', value: 'ISV_VIRTUAL' }
+  { label: "正常注册", value: "NORMAL" },
+  { label: "ISV代理", value: "ISV_AGENT" },
+  { label: "ISV虚拟", value: "ISV_VIRTUAL" },
 ];
 
 // 签约出资方式选项
 export const SIGN_FUND_WAY_OPTIONS = [
-  { label: '余额', value: 'BALANCE' },
-  { label: '信用', value: 'CREDIT' },
-  { label: '企业卡', value: 'CORPORATE_CARD' },
-  { label: '企业发薪', value: 'ENT_FA_EXPRESS' },
-  { label: '个人垫付', value: 'GEN_PER_ADV_PAY' },
-  { label: '卡支付 ledger', value: 'CARD_PAY_LEDGER' },
-  { label: '个人支付宝信用', value: 'PER_ALI_CREDIT' }
+  { label: "余额", value: "BALANCE" },
+  { label: "信用", value: "CREDIT" },
+  { label: "企业卡", value: "CORPORATE_CARD" },
+  { label: "企业发薪", value: "ENT_FA_EXPRESS" },
+  { label: "个人垫付", value: "GEN_PER_ADV_PAY" },
+  { label: "卡支付 ledger", value: "CARD_PAY_LEDGER" },
+  { label: "个人支付宝信用", value: "PER_ALI_CREDIT" },
 ];
 
 // 状态标签类型
 export const STATUS_TAG_TYPE = {
-  'ENTERPRISE_APPLICATION': 'info',
-  'ENTERPRISE_CREATE': 'info',
-  'ENTERPRISE_ACTIVATED': 'success',
-  'ENTERPRISE_UNSIGN': 'warning',
-  'ENTERPRISE_WITHDRAW': 'danger',
-  'ENTERPRISE_AUTH': 'success',
-  'ENTERPRISE_AUTH_REJECTED': 'danger'
+  ENTERPRISE_APPLICATION: "info",
+  ENTERPRISE_CREATE: "info",
+  ENTERPRISE_ACTIVATED: "success",
+  ENTERPRISE_UNSIGN: "warning",
+  ENTERPRISE_WITHDRAW: "danger",
+  ENTERPRISE_AUTH: "success",
+  ENTERPRISE_AUTH_REJECTED: "danger",
 };
 
 // 状态标签文本
 export const STATUS_LABEL = {
-  'ENTERPRISE_APPLICATION': '企业申请',
-  'ENTERPRISE_CREATE': '已创建',
-  'ENTERPRISE_ACTIVATED': '已签约',
-  'ENTERPRISE_UNSIGN': '已解约',
-  'ENTERPRISE_WITHDRAW': '已注销',
-  'ENTERPRISE_AUTH': '认证通过',
-  'ENTERPRISE_AUTH_REJECTED': '认证失败'
+  ENTERPRISE_APPLICATION: "企业申请",
+  ENTERPRISE_CREATE: "已创建",
+  ENTERPRISE_ACTIVATED: "已签约",
+  ENTERPRISE_UNSIGN: "已解约",
+  ENTERPRISE_WITHDRAW: "已注销",
+  ENTERPRISE_AUTH: "认证通过",
+  ENTERPRISE_AUTH_REJECTED: "认证失败",
 };
 
 const enterpriseApi = {
@@ -57,13 +56,13 @@ const enterpriseApi = {
    */
   list: (pageNo: number = 1, pageSize: number = 20, search: Record<string, any> = {}) => {
     return request({
-      url: '/payment/enterprise',
-      method: 'get',
+      url: "/payment/enterprise",
+      method: "get",
       params: {
         page_no: pageNo,
         page_size: pageSize,
-        ...search
-      }
+        ...search,
+      },
     });
   },
 
@@ -86,20 +85,20 @@ const enterpriseApi = {
   detail: (enterpriseId: string) => {
     return request({
       url: `/payment/enterprise/${enterpriseId}`,
-      method: 'get'
+      method: "get",
     });
   },
 
   /**
    * 更新企业信息
    * @param enterpriseId 企业ID
-   * @param data 更新信息
+   * @param data 更新信息 EnterpriseUpdateSchema
    */
-  update: (enterpriseId: string, data: EnterpriseUpdateSchema) => {
+  update: (enterpriseId: string, data: any) => {
     return request({
       url: `/payment/enterprise/${enterpriseId}`,
-      method: 'put',
-      data
+      method: "put",
+      data,
     });
   },
 
@@ -107,15 +106,11 @@ const enterpriseApi = {
    * 申请企业邀请码
    * @param data 申请邀请码数据
    */
-  applyInvite: (data: {
-    identity_type: string;
-    identity?: string;
-    identity_open_id?: string;
-  }) => {
+  applyInvite: (data: { identity_type: string; identity?: string; identity_open_id?: string }) => {
     return request({
       url: `/payment/enterprise/invite`,
-      method: 'post',
-      data
+      method: "post",
+      data,
     });
   },
 
@@ -126,7 +121,7 @@ const enterpriseApi = {
   unsign: (enterpriseId: string) => {
     return request({
       url: `/payment/enterprise/${enterpriseId}/unsign`,
-      method: 'post'
+      method: "post",
     });
   },
 
@@ -137,9 +132,9 @@ const enterpriseApi = {
   delete: (enterpriseId: string) => {
     return request({
       url: `/payment/enterprise/${enterpriseId}/delete`,
-      method: 'post'
+      method: "post",
     });
-  }
+  },
 };
 
-export default enterpriseApi;
+export default enterpriseApi;

+ 1 - 1
frontend/src/layouts/components/AppLogo/index.vue

@@ -2,7 +2,7 @@
   <div class="logo">
     <transition enter-active-class="animate__animated animate__fadeInLeft">
       <router-link :key="+collapse" class="wh-full flex-center" to="/">
-        <img :src="configStore.configData.sys_web_logo.config_value" class="w50px h50px" />
+<!--        <img :src="configStore.configData.sys_web_logo.config_value" class="w50px h50px" />-->
         <span v-if="!collapse" class="title">
           {{ configStore.configData.sys_web_title.config_value }}
         </span>

+ 0 - 1
frontend/src/store/index.ts

@@ -33,7 +33,6 @@ import { useDictStoreHook } from "./modules/dict.store";
 import { useConfigStoreHook } from "./modules/config.store";
 import { useNoticeStoreHook } from "./modules/notice.store";
 import { useTagsViewStore } from "./modules/tags-view.store";
-import { useEnterpriseStoreHook } from "./modules/enterprise.store";
 
 export interface RefreshCacheOptions {
   /** 需要刷新的字典类型列表,不传则不刷新字典 */

+ 8 - 9
frontend/src/store/modules/enterprise.store.ts

@@ -2,10 +2,8 @@ import { defineStore } from "pinia";
 
 import EnterpriseAPI from "@/api/module_payment/enterprise";
 
-
-const ENTERPRISE_LIST_KEY = 'enterpriseList';
-const CURRENT_ENTERPRISE_KEY = 'currentEnterprise';
-
+const ENTERPRISE_LIST_KEY = "enterpriseList";
+const CURRENT_ENTERPRISE_KEY = "currentEnterprise";
 
 export interface EnterpriseItem {
   enterprise_id: string;
@@ -14,7 +12,6 @@ export interface EnterpriseItem {
   short_name?: string;
 }
 
-
 export const useEnterpriseStore = defineStore("enterprise", {
   state: () => ({
     enterpriseList: [] as EnterpriseItem[],
@@ -26,7 +23,7 @@ export const useEnterpriseStore = defineStore("enterprise", {
       if (state.enterpriseList.length > 0) {
         return state.enterpriseList;
       }
-      return JSON.parse(sessionStorage.getItem(ENTERPRISE_LIST_KEY) || '[]');
+      return JSON.parse(sessionStorage.getItem(ENTERPRISE_LIST_KEY) || "[]");
     },
     getCurrentEnterprise(state) {
       if (state.currentEnterprise) {
@@ -63,11 +60,11 @@ export const useEnterpriseStore = defineStore("enterprise", {
 
     async loadEnterpriseList() {
       // 先从sessionStorage获取企业列表,避免重复请求
-      let enterpriseListJson = sessionStorage.getItem(ENTERPRISE_LIST_KEY);
+      const enterpriseListJson = sessionStorage.getItem(ENTERPRISE_LIST_KEY);
       if (enterpriseListJson) {
         this.enterpriseList = JSON.parse(enterpriseListJson);
       } else {
-        this.enterpriseList = await this.fetchEnterpriseList() || [];
+        this.enterpriseList = (await this.fetchEnterpriseList()) || [];
         if (this.enterpriseList.length > 0) {
           sessionStorage.setItem(ENTERPRISE_LIST_KEY, JSON.stringify(this.enterpriseList));
         }
@@ -75,7 +72,9 @@ export const useEnterpriseStore = defineStore("enterprise", {
     },
 
     setCurrentEnterprise(enterpriseId: string) {
-      this.currentEnterprise = this.getEnterpriseList.find((e: EnterpriseItem) => e.enterprise_id === enterpriseId) || null;
+      this.currentEnterprise =
+        this.getEnterpriseList.find((e: EnterpriseItem) => e.enterprise_id === enterpriseId) ||
+        null;
       if (this.currentEnterprise) {
         sessionStorage.setItem(CURRENT_ENTERPRISE_KEY, JSON.stringify(this.currentEnterprise));
       }

+ 51 - 51
frontend/src/views/enterprise/select.vue

@@ -3,31 +3,24 @@
     <div class="select-container">
       <h1 class="title">选择企业</h1>
       <p class="subtitle">请选择您要管理的企业</p>
-      
+
       <div v-if="loading" class="loading">
-        <el-spinner type="circle" :size="40" />
+<!--        <el-spinner type="circle" :size="40" />-->
         <p>加载企业列表中...</p>
       </div>
-      
+
       <div v-else-if="error" class="error">
-        <el-alert
-          :title="error"
-          type="error"
-          show-icon
-          :closable="false"
-        />
-        <el-button type="primary" @click="fetchEnterpriseList">重新加载</el-button>
+        <ElAlert :title="error" type="error" show-icon :closable="false" />
+        <ElButton type="primary" @click="fetchEnterpriseList">重新加载</ElButton>
       </div>
-      
+
       <div v-else-if="enterprises.length === 0" class="empty">
-        <el-empty
-          description="暂无企业"
-        />
-        <el-button type="primary" @click="createEnterprise">创建企业</el-button>
+        <ElEmpty description="暂无企业" />
+        <ElButton type="primary" @click="createEnterprise">创建企业</ElButton>
       </div>
-      
+
       <div v-else class="enterprise-list">
-        <el-card
+        <ElCard
           v-for="enterprise in enterprises"
           :key="enterprise.enterprise_id"
           class="enterprise-card"
@@ -35,35 +28,42 @@
         >
           <div class="card-header">
             <h3 class="enterprise-name">{{ enterprise.name }}</h3>
-            <el-tag
+            <ElTag
               :type="enterprise.status === 'ENTERPRISE_ACTIVATED' ? 'success' : 'info'"
               size="small"
             >
               {{ getStatusText(enterprise.status) }}
-            </el-tag>
+            </ElTag>
           </div>
           <div class="card-body">
             <p class="enterprise-id">企业ID: {{ enterprise.enterprise_id }}</p>
-            <p class="enterprise-short-name">简称: {{ enterprise.short_name || '-' }}</p>
-            <p class="enterprise-account">支付宝账号: {{ enterprise.account_id || '-' }}</p>
+            <p class="enterprise-short-name">简称: {{ enterprise.short_name || "-" }}</p>
+            <p class="enterprise-account">支付宝账号: {{ enterprise.account_id || "-" }}</p>
           </div>
-        </el-card>
+        </ElCard>
       </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted } from 'vue';
-import { useRouter } from 'vue-router';
-import { ElCard, ElTag, ElButton, ElEmpty, ElAlert, ElSpinner } from 'element-plus';
-import type { EnterpriseListOutSchema } from '@/api/module_payment/enterprise';
-import { enterpriseApi } from '@/api/module_payment/enterprise';
+import { ref, onMounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElCard, ElTag, ElButton, ElEmpty, ElAlert } from "element-plus";
+import enterpriseApi from "@/api/module_payment/enterprise";
 
 const router = useRouter();
 
 // 企业列表
-const enterprises = ref<EnterpriseListOutSchema[]>([]);
+const enterprises = ref<
+  {
+    enterprise_id: string;
+    account_id: string;
+    name: string;
+    short_name?: string;
+    status: string;
+  }[]
+>([]);
 // 加载状态
 const loading = ref(true);
 // 错误信息
@@ -72,12 +72,12 @@ const error = ref<string | null>(null);
 // 获取企业状态文本
 const getStatusText = (status: string): string => {
   const statusMap: Record<string, string> = {
-    'ENTERPRISE_CREATE': '已创建',
-    'ENTERPRISE_ACTIVATED': '已激活',
-    'ENTERPRISE_UNSIGN': '已解约',
-    'ENTERPRISE_WITHDRAW': '已注销',
-    'ENTERPRISE_AUTH': '认证中',
-    'ENTERPRISE_AUTH_REJECTED': '认证失败'
+    ENTERPRISE_CREATE: "已创建",
+    ENTERPRISE_ACTIVATED: "已激活",
+    ENTERPRISE_UNSIGN: "已解约",
+    ENTERPRISE_WITHDRAW: "已注销",
+    ENTERPRISE_AUTH: "认证中",
+    ENTERPRISE_AUTH_REJECTED: "认证失败",
   };
   return statusMap[status] || status;
 };
@@ -86,33 +86,33 @@ const getStatusText = (status: string): string => {
 const fetchEnterpriseList = async () => {
   loading.value = true;
   error.value = null;
-  
+
   try {
-    const response = await enterpriseApi.list(1, 100);
-    if (response.code === 200) {
-      enterprises.value = response.data?.items || [];
-    } else {
-      error.value = response.msg || '获取企业列表失败';
-    }
+    // const response = await enterpriseApi.list(1, 100);
+    // if (response.code === 200) {
+    //   enterprises.value = response.data?.items || [];
+    // } else {
+    //   error.value = response.msg || "获取企业列表失败";
+    // }
   } catch (err) {
-    error.value = '网络错误,请稍后重试';
-    console.error('获取企业列表失败:', err);
+    error.value = "网络错误,请稍后重试";
+    console.error("获取企业列表失败:", err);
   } finally {
     loading.value = false;
   }
 };
 
 // 选择企业
-const selectEnterprise = (enterprise: EnterpriseListOutSchema) => {
+const selectEnterprise = (enterprise: any) => {
   // 存储选择的企业信息
-  localStorage.setItem('selectedEnterprise', JSON.stringify(enterprise));
-  // 跳转到主页
-  router.push('/dashboard');
+  // localStorage.setItem("selectedEnterprise", JSON.stringify(enterprise));
+  // // 跳转到主页
+  // router.push("/dashboard");
 };
 
 // 创建企业
 const createEnterprise = () => {
-  router.push('/enterprise/create');
+  router.push("/enterprise/create");
 };
 
 // 页面挂载时获取企业列表
@@ -233,13 +233,13 @@ onMounted(() => {
   .select-container {
     padding: 24px;
   }
-  
+
   .title {
     font-size: 20px;
   }
-  
+
   .enterprise-list {
     grid-template-columns: 1fr;
   }
 }
-</style>
+</style>

+ 35 - 19
frontend/src/views/module_payment/account/components/AccountOverview.vue

@@ -38,13 +38,16 @@
       </template> -->
 
       <div class="quick-actions">
-        <div class="action-item" :class="{ disabled: authorizeStatus === 'AUTHORIZED' || !!accountData.account_book_id }">
+        <div
+          class="action-item"
+          :class="{ disabled: authorizeStatus === 'AUTHORIZED' || !!accountData.account_book_id }"
+        >
           <div class="action-icon">
             <el-icon :size="32"><Document /></el-icon>
           </div>
           <div class="action-title">转账授权签约</div>
           <div class="action-desc">
-            {{ authorizeStatus === 'AUTHORIZED' ? '已签约' : '前往签约' }}
+            {{ authorizeStatus === "AUTHORIZED" ? "已签约" : "前往签约" }}
           </div>
           <el-button
             v-hasPerm="['module_payment:account:authorize']"
@@ -52,11 +55,14 @@
             :disabled="authorizeStatus === 'AUTHORIZED'"
             @click="$emit('goTab', 'authorize')"
           >
-            {{ authorizeStatus === 'AUTHORIZED' ? '已完成' : '去签约' }}
+            {{ authorizeStatus === "AUTHORIZED" ? "已完成" : "去签约" }}
           </el-button>
         </div>
 
-        <div class="action-item" :class="{ disabled: authorizeStatus === 'AUTHORIZED' || !!accountData.account_book_id }">
+        <div
+          class="action-item"
+          :class="{ disabled: authorizeStatus === 'AUTHORIZED' || !!accountData.account_book_id }"
+        >
           <div class="action-icon">
             <el-icon :size="32"><Coin /></el-icon>
           </div>
@@ -72,11 +78,11 @@
             :disabled="authorizeStatus === 'AUTHORIZED' || !!accountData.account_book_id"
             @click="$emit('goTab', 'create')"
           >
-            {{ accountData.account_book_id ? '已开通' : '去开通' }}
+            {{ accountData.account_book_id ? "已开通" : "去开通" }}
           </el-button>
         </div>
 
-        <div class="action-item" :class="{ disabled: !accountData.account_book_id}">
+        <div class="action-item" :class="{ disabled: !accountData.account_book_id }">
           <div class="action-icon">
             <el-icon :size="32"><Wallet /></el-icon>
           </div>
@@ -96,7 +102,10 @@
           </el-button>
         </div>
 
-        <div class="action-item" :class="{ disabled: !accountData.account_book_id || accountData.balance <= 0 }">
+        <div
+          class="action-item"
+          :class="{ disabled: !accountData.account_book_id || accountData.balance <= 0 }"
+        >
           <div class="action-icon">
             <el-icon :size="32"><Money /></el-icon>
           </div>
@@ -138,7 +147,7 @@
             <div class="stat-label">账户状态</div>
             <div class="stat-value">
               <el-tag :type="getAccountStatusType(accountData.status)">
-                {{ accountData.status || '未开通' }}
+                {{ accountData.status || "未开通" }}
               </el-tag>
             </div>
           </div>
@@ -150,7 +159,7 @@
           <div class="stat-item">
             <div class="stat-label">账户号</div>
             <div class="stat-value text-truncate">
-              {{ accountData.account_book_id || '-' }}
+              {{ accountData.account_book_id || "-" }}
             </div>
           </div>
         </el-card>
@@ -160,9 +169,7 @@
         <el-card shadow="hover">
           <div class="stat-item">
             <div class="stat-label">账户余额</div>
-            <div class="stat-value balance-value">
-              ¥{{ accountData.balance || '0.00' }}
-            </div>
+            <div class="stat-value balance-value">¥{{ accountData.balance || "0.00" }}</div>
           </div>
         </el-card>
       </el-col>
@@ -176,7 +183,7 @@
         </div>
       </template>
 
-      <el-table :data="recentTransfers" border stripe v-loading="loading">
+      <el-table v-loading="loading" :data="recentTransfers" border stripe>
         <template #empty>
           <el-empty description="暂无交易记录" />
         </template>
@@ -185,13 +192,18 @@
           <template #default="{ row }">
             <span class="amount">
               <!-- {{ row.payee_info ? '-' : '+' }}¥{{ row.amount }} -->
-                ¥{{ row.amount }}
+              ¥{{ row.amount }}
             </span>
           </template>
         </el-table-column>
-        <el-table-column prop="payee_info.name" label="收款方" min-width="120" show-overflow-tooltip>
+        <el-table-column
+          prop="payee_info.name"
+          label="收款方"
+          min-width="120"
+          show-overflow-tooltip
+        >
           <template #default="{ row }">
-            {{ row.payee_info?.name || '-' }}
+            {{ row.payee_info?.name || "-" }}
           </template>
         </el-table-column>
         <el-table-column prop="status" label="状态" min-width="100">
@@ -233,9 +245,13 @@ const accountData = ref<{
 }>({});
 const recentTransfers = ref<any[]>([]);
 
-const currentEnterpriseId = computed(() => props.enterpriseId || enterpriseStore.getCurrentEnterprise?.enterprise_id);
+const currentEnterpriseId = computed(
+  () => props.enterpriseId || enterpriseStore.getCurrentEnterprise?.enterprise_id
+);
 
-const authorizeStatus = computed(() =>  !!accountData.value.account_book_id ? "AUTHORIZED" : 'PENDING');
+const authorizeStatus = computed(() =>
+  accountData.value.account_book_id ? "AUTHORIZED" : "PENDING"
+);
 
 const currentStep = computed(() => {
   if (authorizeStatus.value !== "AUTHORIZED") return 0;
@@ -271,7 +287,7 @@ const accountStatusText = computed(() => {
     FROZEN: "冻结",
     CLOSED: "关闭",
   };
-  return map[accountData.value.status] || accountData.value.status;
+  return map[accountData.value.status || ""] || accountData.value.status;
 });
 
 const depositStatusText = computed(() => {

+ 3 - 1
pyproject.toml

@@ -2,4 +2,6 @@
 name = "payment-platform"
 version = "0.1.0"
 requires-python = ">=3.10"
-dependencies = []
+dependencies = [
+    "alipay-sdk-python==3.7.1098",
+]

+ 75 - 0
uv.lock

@@ -2,7 +2,82 @@ version = 1
 revision = 3
 requires-python = ">=3.10"
 
+[[package]]
+name = "alipay-sdk-python"
+version = "3.7.1098"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pycryptodome" },
+    { name = "rsa" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7f/9a/50c461add0e59d671ff58ebbb44a38d8406a93d9f2269dbbc32e53d3fa75/alipay_sdk_python-3.7.1098.tar.gz", hash = "sha256:08590765f7121ee2ecc5fb6ae46cd79064a9f0f794bb1879d02e02354e58af3b", size = 9779976, upload-time = "2026-04-09T04:34:10.139Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1c/7c/b522405de4a8ef8609ee584cd8ac9e6e106af4f0f1b7dc5df1350335c1f3/alipay_sdk_python-3.7.1098-py3-none-any.whl", hash = "sha256:e3dedef01221815ad1e0bd290603373de7a26caaba487ee598dfbb71c271ae83", size = 30568408, upload-time = "2026-04-09T04:34:03.636Z" },
+]
+
 [[package]]
 name = "payment-platform"
 version = "0.1.0"
 source = { virtual = "." }
+dependencies = [
+    { name = "alipay-sdk-python" },
+]
+
+[package.metadata]
+requires-dist = [{ name = "alipay-sdk-python", specifier = "==3.7.1098" }]
+
+[[package]]
+name = "pyasn1"
+version = "0.6.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" },
+]
+
+[[package]]
+name = "pycryptodome"
+version = "3.23.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" },
+    { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" },
+    { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" },
+    { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" },
+    { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" },
+    { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" },
+    { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" },
+    { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" },
+    { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" },
+    { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" },
+    { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" },
+    { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" },
+    { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" },
+    { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" },
+    { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" },
+    { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" },
+    { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" },
+    { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" },
+    { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" },
+    { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" },
+    { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" },
+    { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" },
+    { url = "https://files.pythonhosted.org/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886, upload-time = "2025-05-17T17:21:20.614Z" },
+    { url = "https://files.pythonhosted.org/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151, upload-time = "2025-05-17T17:21:22.666Z" },
+    { url = "https://files.pythonhosted.org/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461, upload-time = "2025-05-17T17:21:25.225Z" },
+    { url = "https://files.pythonhosted.org/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440, upload-time = "2025-05-17T17:21:27.991Z" },
+    { url = "https://files.pythonhosted.org/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005, upload-time = "2025-05-17T17:21:31.37Z" },
+]
+
+[[package]]
+name = "rsa"
+version = "4.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pyasn1" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
+]