husenlin 1 mesiac pred
rodič
commit
240b7f31c8

+ 0 - 2
backend/app/plugin/module_payment/account/__init__.py

@@ -25,8 +25,6 @@ from .schema import (
     AccountTransferSchema,
     AccountTransferBatchSchema,
     AccountTransferBatchOutSchema,
-    TransferTaskOutSchema,
-    TransferQueueStatusSchema,
     AccountWithdrawSchema,
     DepositOutSchema,
     PayeeInfoSchema,

+ 6 - 10
backend/app/plugin/module_payment/account/controller.py

@@ -1,6 +1,6 @@
-from typing import Annotated, Any
+from typing import Annotated, Any, Optional, Dict
 
-from fastapi import APIRouter, Depends, Path, Query
+from fastapi import APIRouter, Body, Depends, Path, Query
 from fastapi.responses import JSONResponse
 
 from app.api.v1.module_system.auth.schema import AuthSchema
@@ -8,6 +8,7 @@ from app.common.response import ResponseSchema, SuccessResponse
 from app.core.dependencies import AuthPermission
 from app.core.logger import log
 from app.core.router_class import OperationLogRoute
+from app.core.base_params import PaginationQueryParam
 
 from .schema import (
     AccountAuthorizeApplySchema,
@@ -18,14 +19,9 @@ from .schema import (
     AccountOperationOutSchema,
     AccountQuerySchema,
     AccountTransferSchema,
-    AccountTransferOutSchema,
-    AccountTransferBatchSchema,
-    AccountTransferBatchOutSchema,
-    TransferResultSchema,
     TransferListOutSchema,
     TransferOutSchema,
     TransferTaskOutSchema,
-    TransferQueueStatusSchema,
     ConsumeDetailOutSchema,
 )
 from .service import AccountService
@@ -157,12 +153,12 @@ async def transfer_detail_controller(
 )
 async def transfer_list_controller(
     auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:account:transfer:list"]))],
-    page_no: Annotated[int, Query(description="页码")] = 1,
-    page_size: Annotated[int, Query(description="每页数量")] = 20,
+    page: Annotated[PaginationQueryParam, Depends()],
+    search: Annotated[Dict, Depends()],
 ) -> JSONResponse:
     """查询转账记录列表"""
     result = await AccountService.transfer_list_service(
-        auth=auth, page_no=page_no, page_size=page_size
+        auth=auth, page_no=page.page_no, page_size=page.page_size, order_by=page.order_by, search=search
     )
     return SuccessResponse(data=result, msg="查询转账记录列表成功")
 

+ 6 - 6
backend/app/plugin/module_payment/account/schema.py

@@ -127,7 +127,7 @@ class AccountWithdrawSchema(BaseModel):
 
     enterprise_id: str = Field(description="企业ID")
     account_book_id: str = Field(description="资金专户号")
-    amount: float = Field(gt=0, description="提现金额")
+    amount: Decimal = Field(max_digits=10, decimal_places=2, gt=0, description="提现金额")
     out_biz_no: str = Field(description="商家侧订单号")
 
 
@@ -220,7 +220,7 @@ class TransferTaskSchema(BaseModel):
     enterprise_id: str = Field(description="企业ID")
     account_book_id: str = Field(description="资金专户号")
     out_biz_no: str = Field(description="商家侧订单号")
-    amount: float = Field(gt=0, description="转账金额")
+    amount: Decimal = Field(max_digits=10, decimal_places=2, gt=0, description="转账金额")
     order_title: str = Field(description="转账标题")
     payee_info: PayeeInfoSchema = Field(description="收款方信息")
     priority: str = Field(default="NORMAL", description="优先级: HIGH/NORMAL/LOW")
@@ -236,7 +236,7 @@ class TransferTaskOutSchema(BaseModel):
     status: str = Field(description="任务状态")
     enterprise_id: str = Field(description="企业ID")
     out_biz_no: str = Field(description="商家侧订单号")
-    amount: float = Field(description="转账金额")
+    amount: Decimal = Field(max_digits=10, decimal_places=2, gt=0, description="转账金额")
     priority: str = Field(description="优先级")
     created_at: str = Field(description="创建时间")
 
@@ -259,11 +259,11 @@ class TransferOutSchema(BaseModel):
 
     model_config = ConfigDict(from_attributes=True)
 
-    id: int = Field(description="主键ID")
+    # id: int = Field(description="主键ID")
     enterprise_id: Optional[str] = Field(default=None, description="所属企业ID")
     out_biz_no: Optional[str] = Field(default=None, description="商家侧订单号")
     account_book_id: Optional[str] = Field(default=None, description="付款方资金专户号")
-    amount: Optional[float] = Field(default=None, description="转账金额")
+    amount: Decimal = Field(max_digits=10, decimal_places=2, gt=0, description="转账金额")
     order_title: Optional[str] = Field(default=None, description="转账标题")
     payee_info: Optional[dict] = Field(default=None, description="收款方信息")
     status: str = Field(description="转账状态")
@@ -314,7 +314,7 @@ class WithdrawOutSchema(BaseModel):
     enterprise_id: Optional[str] = Field(default=None, description="所属企业ID")
     out_biz_no: Optional[str] = Field(default=None, description="商家侧订单号")
     account_book_id: Optional[str] = Field(default=None, description="资金专户号")
-    amount: Optional[float] = Field(default=None, description="提现金额")
+    amount: Optional[Decimal] = Field(default=None, description="提现金额")
     status: str = Field(description="提现状态")
     order_no: Optional[str] = Field(default=None, description="支付宝提现单号")
     error_code: Optional[str] = Field(default=None, description="错误码")

+ 11 - 8
backend/app/plugin/module_payment/account/service.py

@@ -9,6 +9,7 @@ from app.core.alipay import AlipayClient
 from app.core.exceptions import CustomException
 from app.core.logger import log
 from app.utils.snowflake import get_snowflake_id_str
+from app.plugin.module_payment.enterprise.crud import EnterpriseCRUD
 
 from .crud import AccountCRUD, TransferCRUD, DepositCRUD, WithdrawCRUD
 from .enums import (
@@ -221,15 +222,16 @@ class AccountService:
         from alipay.aop.api.domain.BankCardExtInfoDTO import (
             BankCardExtInfoDTO,
         )
-        
-        # 检查企业是否存在
-        crud = AccountCRUD(auth)
-        enterprise = await crud.get_by_enterprise_id(data.enterprise_id)
 
+        # 检查企业是否存在
+        enterprise = await EnterpriseCRUD(auth).get_by_enterprise_id(data.enterprise_id)
         if not enterprise:
             raise CustomException(msg="企业不存在")
+        if not enterprise.tenant_id != auth.tenant_id:
+            raise CustomException(msg="无权限操作")
         
-        account = await crud.get_by_enterprise_id(data.enterprise_id)
+        # 检查资金专户是否存在
+        account = await AccountCRUD(auth).get_by_enterprise_id(data.enterprise_id)
         if not account:
             raise CustomException(msg="资金专户不存在")
 
@@ -247,7 +249,8 @@ class AccountService:
         payee_info.identity = data.payee_info.identity
         if data.payee_info.bankcard_ext_info:
             payee_info.bankcard_ext_info = BankCardExtInfoDTO.from_alipay_dict(
-                    data.payee_info.bankcard_ext_info.model_dump(exclude_none=True))
+                    data.payee_info.bankcard_ext_info.model_dump(exclude_none=True)
+            )
 
         model.payee_info = payee_info
 
@@ -278,7 +281,6 @@ class AccountService:
             "status": result.status,
             "order_no": result.order_no,
             "fund_order_id": result.fund_order_id,
-            # "ext_info": data.ext_info,
         }
 
         await transfer_crud.create(transfer_data)
@@ -421,6 +423,7 @@ class AccountService:
         auth: AuthSchema,
         page_no: int = 1,
         page_size: int = 20,
+        order_by: list[dict[str, str]] | None = None,
         search: dict | None = None,
     ) -> dict:
         """
@@ -431,7 +434,7 @@ class AccountService:
         return await crud.page(
             offset=offset,
             limit=page_size,
-            order_by=[{"id": "desc"}],
+            order_by=order_by or [{"id": "desc"}],
             search=search or {},
             out_schema=TransferListOutSchema,
         )

+ 2 - 2
backend/app/plugin/module_payment/department/service.py

@@ -3,7 +3,7 @@ from typing import Any
 from app.api.v1.module_system.auth.schema import AuthSchema
 from app.core.exceptions import CustomException
 from app.core.logger import log
-from app.utils.snowflake import get_snowflake_id
+from app.utils.snowflake import get_snowflake_id_str
 
 from .crud import DepartmentCRUD
 from .enums import DepartmentStatusEnum
@@ -26,7 +26,7 @@ class DepartmentService:
         crud = DepartmentCRUD(auth)
 
         department_data = data.model_dump(exclude_none=True)
-        department_data["department_id"] = str(get_snowflake_id())
+        department_data["department_id"] = get_snowflake_id_str(auth.tenant_id)
         department_data["status"] = DepartmentStatusEnum.DEPARTMENT_ACTIVE.value
 
         if department_data.get("parent_id"):

+ 11 - 9
frontend/src/layouts/components/NavBar/components/EnterpriseSwitcher.vue

@@ -11,13 +11,13 @@
       <template #dropdown>
         <el-dropdown-menu>
           <el-dropdown-item
-            v-for="ent in userStore.enterpriseList"
+            v-for="ent in enterpriseStore.loadEnterpriseList()"
             :key="ent.enterprise_id"
             :command="ent.enterprise_id"
             :class="{ 'is-active': ent.enterprise_id === userStore.currentEnterprise?.enterprise_id }"
           >
             <div class="enterprise-item">
-              <span class="enterprise-item__name">{{ ent.name || ent.enterprise_id }}</span>
+              <span class="enterprise-item__name">{{ ent.name }}</span>
               <el-icon v-if="ent.enterprise_id === userStore.currentEnterprise?.enterprise_id" class="enterprise-item__check">
                 <Check />
               </el-icon>
@@ -31,20 +31,22 @@
 
 <script setup lang="ts">
 import { computed } from "vue";
-import { useUserStore } from "@/store";
+import { useEnterpriseStore } from "@/store/modules/enterprise.store";
 import { OfficeBuilding, ArrowDown, Check } from "@element-plus/icons-vue";
 
-const userStore = useUserStore();
+const enterpriseStore = useEnterpriseStore();
 
-const currentEnterpriseName = computed(() => {
-  if (!userStore.currentEnterprise) {
-    return "请选择企业";
+const currentEnterprise = computed(() => {
+  let current = enterpriseStore.getCurrentEnterprise();
+  if (!current && enterpriseStore.hasEnterprise) {
+    enterpriseStore.setCurrentEnterprise(enterpriseStore.getEnterpriseList[0].enterprise_id);
+    current = enterpriseStore.getCurrentEnterprise();
   }
-  return userStore.currentEnterprise.name || userStore.currentEnterprise.enterprise_id;
+  return current;
 });
 
 function handleSwitch(enterpriseId: string) {
-  userStore.setCurrentEnterprise(enterpriseId);
+  enterpriseStore.setCurrentEnterprise(enterpriseId);
 }
 </script>
 

+ 34 - 21
frontend/src/store/modules/enterprise.store.ts

@@ -2,23 +2,37 @@ import { defineStore } from "pinia";
 
 import EnterpriseAPI from "@/api/module_payment/enterprise";
 
+
+const ENTERPRISE_LIST_KEY = 'enterpriseList';
+const CURRENT_ENTERPRISE_KEY = 'currentEnterprise';
+
+
 export interface EnterpriseItem {
-  account_id: string;
   enterprise_id: string;
+  account_id: string;
   name: string;
   short_name?: string;
 }
 
+
 export const useEnterpriseStore = defineStore("enterprise", {
   state: () => ({
-    // enterpriseList: [] as EnterpriseItem[],
-    // currentEnterprise: null as EnterpriseItem | null,
+    enterpriseList: [] as EnterpriseItem[],
+    currentEnterprise: null as EnterpriseItem | null,
   }),
 
   getters: {
-    // getEnterpriseList: (state) => state.enterpriseList,
-    // getCurrentEnterprise: (state) => state.currentEnterprise,
-    // hasEnterprise: (state) => state.enterpriseList.length > 0,
+    getEnterpriseList(state) {
+      state.enterpriseList = state.enterpriseList || JSON.parse(sessionStorage.getItem(ENTERPRISE_LIST_KEY) || '[]');
+      return state.enterpriseList;
+    },
+    getCurrentEnterprise(state) {
+      state.currentEnterprise = state.currentEnterprise || JSON.parse(sessionStorage.getItem(CURRENT_ENTERPRISE_KEY) || '')
+      return state.currentEnterprise;
+    },
+    hasEnterprise() {
+      return this.getEnterpriseList.length > 0;
+    },
   },
 
   actions: {
@@ -34,10 +48,6 @@ export const useEnterpriseStore = defineStore("enterprise", {
           short_name: item.short_name,
           ...item,
         }));
-
-        // if (!this.currentEnterprise && this.enterpriseList.length > 0) {
-        //   this.setCurrentEnterprise(this.enterpriseList[0].enterprise_id);
-        // }
       } catch (error) {
         console.error("获取企业列表失败:", error);
       }
@@ -45,26 +55,29 @@ export const useEnterpriseStore = defineStore("enterprise", {
 
     async loadEnterpriseList() {
       // 先从sessionStorage获取企业列表,避免重复请求
-      const enterpriseList = sessionStorage.getItem('enterpriseList');
-      if (enterpriseList) {
-        this.enterpriseList = JSON.parse(enterpriseList);
-        return;
+      let enterpriseListJson = sessionStorage.getItem(ENTERPRISE_LIST_KEY);
+      if (enterpriseListJson) {
+        this.enterpriseList = JSON.parse(enterpriseListJson);
+      } else {
+        this.enterpriseList = await this.fetchEnterpriseList() || [];
+        if (this.enterpriseList.length > 0) {
+          sessionStorage.setItem(ENTERPRISE_LIST_KEY, JSON.stringify(this.enterpriseList));
+        }
       }
-      await this.fetchEnterpriseList();
-      // 缓存企业列表到sessionStorage
-      sessionStorage.setItem('enterpriseList', JSON.stringify(this.enterpriseList));
     },
 
     setCurrentEnterprise(enterpriseId: string) {
-      const enterprise = this.enterpriseList.find((e) => e.enterprise_id === enterpriseId);
-      if (enterprise) {
-        this.currentEnterprise = enterprise;
+      this.currentEnterprise = this.enterpriseList.find((e) => e.enterprise_id === enterpriseId) || null;
+      if (this.currentEnterprise) {
+        sessionStorage.setItem(CURRENT_ENTERPRISE_KEY, JSON.stringify(this.currentEnterprise));
       }
     },
 
     clearEnterprise() {
-      this.enterpriseList = [];
       this.currentEnterprise = null;
+      this.enterpriseList = [];
+      sessionStorage.removeItem(ENTERPRISE_LIST_KEY);
+      sessionStorage.removeItem(CURRENT_ENTERPRISE_KEY);
     },
   },
 });

+ 4 - 6
frontend/src/views/module_payment/enterprise/index.vue

@@ -161,21 +161,19 @@ import { useRouter } from "vue-router";
 import { ElMessage } from "element-plus";
 import { ref, reactive } from "vue";
 import { useEnterpriseStore } from "@/store/modules/enterprise.store";
-import { use } from "echarts";
 
 const router = useRouter();
 
-const { searchRef, contentRef, handleQueryClick, handleResetClick, refreshList } =
-  useCrudList();
+const { searchRef, contentRef, handleQueryClick, handleResetClick, refreshList } = useCrudList();
 const formRef = ref();
 const enterpriseStore = useEnterpriseStore();
 
+const hasEnterprise = computed(() => enterpriseStore.hasEnterprise);
+
 onMounted(async () => {
-  await useEnterpriseStore().fetchEnterpriseList();
+ 
 });
 
-const hasEnterprise = computed(() => enterpriseStore.hasEnterprise);
-
 const searchConfig = reactive<ISearchConfig>({
   permPrefix: "module_payment:enterprise",
   colon: true,