# Implementation Plan: Industry Invoice Platform (行业发票平台) **Date:** 2026-07-08 **Branch:** fapiao **Status:** Planning --- ## Overview Copy the Alipay industry invoice platform (https://fxkp.alipay.com/industry-invoice-platform/) as a new module in the payment platform. 8 pages with full CRUD, import/export, and mock APIs. Multi-tenant + enterprise + admin data scoping on all tables. --- ## 1. Database Tables All tables use `PaymentEnterpriseBaseEntity` (inherits `PaymentTenantBaseEntity` → `PaymentBaseEntity`), providing: `id` (snowflake), `status`, `description`, `created_time`, `updated_time`, `tenant_id`, `enterprise_id`. Flyway migration file: `V1.2__create_pay_invoice_tables.sql` ### 1.1 `pay_invoice_order` — 交易及发票订单 | Column | Type | Constraint | Notes | |---|---|---|---| | id | BIGINT | PK (snowflake) | From PaymentEnterpriseBaseEntity | | status | VARCHAR(10) | DEFAULT '0' | From PaymentEnterpriseBaseEntity | | description | TEXT | | From PaymentEnterpriseBaseEntity | | created_time | TIMESTAMP | NOT NULL DEFAULT NOW() | From PaymentEnterpriseBaseEntity | | updated_time | TIMESTAMP | NOT NULL DEFAULT NOW() | From PaymentEnterpriseBaseEntity | | tenant_id | BIGINT | | From PaymentTenantBaseEntity | | enterprise_id | VARCHAR(64) | | From PaymentEnterpriseBaseEntity | | order_no | VARCHAR(64) | NOT NULL | 订单编号 | | alipay_trade_no | VARCHAR(64) | | 支付宝交易号 | | payment_time | TIMESTAMP | | 支付时间 | | natural_person_name | VARCHAR(64) | | 自然人姓名 | | collection_account_type | VARCHAR(32) | | 收款账户类型: ALIPAY / BANK_CARD | | tax_inclusive_amount | DECIMAL(18,2) | | 含税金额 | | trade_status | VARCHAR(32) | NOT NULL DEFAULT 'PENDING_ASSOCIATE' | 交易状态 | | invoice_no | VARCHAR(64) | | 发票号码 | | invoice_pretax_amount | DECIMAL(18,2) | | 发票税前金额 | | invoice_tax_amount | DECIMAL(18,2) | | 发票税额 | | red_invoice_no | VARCHAR(64) | | 红票号码 | | clerk_name | VARCHAR(64) | | 营业员姓名 | | attachment_url | TEXT | | 附件URL JSON array | Indexes: - `idx_invoice_order_enterprise` ON `(enterprise_id)` - `idx_invoice_order_order_no` ON `(order_no)` - `idx_invoice_order_trade_no` ON `(alipay_trade_no)` - `idx_invoice_order_trade_status` ON `(trade_status)` - `idx_invoice_order_created_time` ON `(created_time)` ### 1.2 `pay_invoice_supplier` — 供应商 | Column | Type | Constraint | Notes | |---|---|---|---| | id | BIGINT | PK (snowflake) | | | status, description, created_time, updated_time, tenant_id, enterprise_id | | | From base entities | | supplier_name | VARCHAR(128) | NOT NULL | 供应商名称 | | account_type | VARCHAR(32) | NOT NULL | ALIPAY_PHONE / ALIPAY_EMAIL | | collection_account | VARCHAR(128) | NOT NULL | 收款账户 | | phone | VARCHAR(20) | | 联系电话 | | confirm_status | VARCHAR(32) | NOT NULL DEFAULT 'PENDING' | CONFIRMED / PENDING / REJECTED | Indexes: - `idx_invoice_supplier_enterprise` ON `(enterprise_id)` - `idx_invoice_supplier_name` ON `(supplier_name)` - `idx_invoice_supplier_confirm_status` ON `(confirm_status)` ### 1.3 `pay_invoice_employee` — 员工管理 | Column | Type | Constraint | Notes | |---|---|---|---| | id | BIGINT | PK (snowflake) | | | status, description, created_time, updated_time, tenant_id, enterprise_id | | | From base entities | | employee_name | VARCHAR(64) | NOT NULL | 员工姓名 | | phone | VARCHAR(20) | NOT NULL | 手机号 | | id_number | VARCHAR(20) | NOT NULL | 身份证号 | | role | VARCHAR(32) | NOT NULL | CLERK / INVOICER / SUPER_ADMIN | | allow_miniapp_supplier | BOOLEAN | DEFAULT FALSE | 允许小程序选供应商 | Indexes: - `idx_invoice_employee_enterprise` ON `(enterprise_id)` - `idx_invoice_employee_role` ON `(role)` ### 1.4 `pay_invoice_product` — 常用商品 | Column | Type | Constraint | Notes | |---|---|---|---| | id | BIGINT | PK (snowflake) | | | status, description, created_time, updated_time, tenant_id, enterprise_id | | | From base entities | | product_name | VARCHAR(128) | NOT NULL | 商品名称 | | category | VARCHAR(32) | NOT NULL | 报废产品分类 | | unit | VARCHAR(32) | | 单位 | | spec | VARCHAR(128) | | 规格 | Indexes: - `idx_invoice_product_enterprise` ON `(enterprise_id)` - `idx_invoice_product_category` ON `(category)` ### 1.5 `pay_invoice_company` — 企业信息 | Column | Type | Constraint | Notes | |---|---|---|---| | id | BIGINT | PK (snowflake) | | | status, description, created_time, updated_time, tenant_id, enterprise_id | | | From base entities | | company_name | VARCHAR(128) | | 企业名称 | | tax_id | VARCHAR(64) | | 税号 | | bank_name | VARCHAR(128) | | 开户银行 | | bank_account | VARCHAR(64) | | 银行账号 | | address | VARCHAR(256) | | 地址 | | phone | VARCHAR(20) | | 电话 | | tax_region | VARCHAR(64) | | 纳税地区 | | issuer | VARCHAR(64) | | 开票人 | | contact_name | VARCHAR(64) | | 业务联系人姓名 | | contact_phone | VARCHAR(20) | | 业务联系人电话 | | monthly_quota | DECIMAL(18,2) | DEFAULT 10000000 | 月度授信额度 | | available_quota | DECIMAL(18,2) | | 可用额度 | | downloaded_count | INTEGER | DEFAULT 0 | 已下载张数 | | used_count | INTEGER | DEFAULT 0 | 已使用张数 | | tax_calculation_method | VARCHAR(32) | DEFAULT 'SIMPLE' | 计税方式 | | default_invoice_type | VARCHAR(32) | DEFAULT 'NORMAL' | 默认票种 | | default_tax_rate | VARCHAR(16) | DEFAULT '1%' | 默认税率 | | audit_after_payment | BOOLEAN | DEFAULT TRUE | 货品审核后付款 | | invite_as_supplier | BOOLEAN | DEFAULT FALSE | 邀请为供应商 | | receipt_plaintext | BOOLEAN | DEFAULT FALSE | 电子回单明文 | | employee_quota_management | BOOLEAN | DEFAULT FALSE | 员工额度管理 | Index: - `idx_invoice_company_enterprise` ON `(enterprise_id)` UNIQUE (one record per enterprise) ### 1.6 `pay_invoice_task` — 任务中心 | Column | Type | Constraint | Notes | |---|---|---|---| | id | BIGINT | PK (snowflake) | | | status, description, created_time, updated_time, tenant_id, enterprise_id | | | From base entities | | task_type | VARCHAR(32) | NOT NULL | 任务类型 | | task_status | VARCHAR(32) | NOT NULL DEFAULT 'PENDING' | PENDING / PROCESSING / SUCCESS / FAILED | | start_time | TIMESTAMP | | 任务开始时间 | | completion_time | TIMESTAMP | | 完成时间 | | product_name | VARCHAR(128) | | 商品名称 | | file_url | VARCHAR(512) | | 导出文件URL | | total_count | INTEGER | DEFAULT 0 | 总记录数 | | success_count | INTEGER | DEFAULT 0 | 成功数 | | fail_count | INTEGER | DEFAULT 0 | 失败数 | | error_message | TEXT | | 错误信息 | Indexes: - `idx_invoice_task_enterprise` ON `(enterprise_id)` - `idx_invoice_task_type` ON `(task_type)` - `idx_invoice_task_status` ON `(task_status)` ### 1.7 `pay_invoice_tax_payment` — 缴税记录 | Column | Type | Constraint | Notes | |---|---|---|---| | id | BIGINT | PK (snowflake) | | | status, description, created_time, updated_time, tenant_id, enterprise_id | | | From base entities | | payment_mode | VARCHAR(32) | NOT NULL | INDIVIDUAL / ENTERPRISE_PROXY | | tax_amount | DECIMAL(18,2) | | 缴税金额 | | tax_period | VARCHAR(32) | | 纳税期间 | | payment_time | TIMESTAMP | | 缴税时间 | | payment_status | VARCHAR(32) | DEFAULT 'UNPAID' | UNPAID / PAID | | enterprise_proxy_enabled | BOOLEAN | DEFAULT FALSE | 是否开通企业代缴 | Indexes: - `idx_invoice_tax_enterprise` ON `(enterprise_id)` - `idx_invoice_tax_status` ON `(payment_status)` --- ## 2. Backend Module Structure ``` module/payment/invoice/ ├── InvoiceModuleConfig.java (optional: module-level @Configuration if needed) ├── order/ # 1. 交易及发票管理 │ ├── controller/ │ │ └── OrderController.java │ ├── dto/ │ │ ├── OrderQueryDTO.java │ │ ├── OrderVO.java │ │ ├── OrderBatchImportDTO.java │ │ ├── OrderBatchCancelDTO.java │ │ └── InvoiceDetailVO.java │ ├── entity/ │ │ └── OrderEntity.java │ ├── enums/ │ │ └── OrderEnums.java (TradeStatus, CollectionAccountType) │ ├── mapper/ │ │ └── OrderMapper.java │ └── service/ │ ├── OrderService.java (list, detail, batchImport, batchCancel, export) │ └── impl/ │ └── OrderServiceImpl.java (optional — if following existing pattern) ├── account/ # 2. 转账账户管理 │ ├── controller/ │ │ └── InvoiceAccountController.java │ ├── dto/ │ │ └── AccountBalanceVO.java │ ├── entity/ │ │ └── InvoiceAccountEntity.java │ ├── mapper/ │ │ └── InvoiceAccountMapper.java │ └── service/ │ └── InvoiceAccountService.java ├── tax/ # 3. 缴税管理 │ ├── controller/ │ │ └── TaxPaymentController.java │ ├── dto/ │ │ ├── TaxPaymentQueryDTO.java │ │ └── TaxPaymentVO.java │ ├── entity/ │ │ └── TaxPaymentEntity.java │ ├── enums/ │ │ └── TaxEnums.java (PaymentMode, PaymentStatus) │ ├── mapper/ │ │ └── TaxPaymentMapper.java │ └── service/ │ └── TaxPaymentService.java ├── employee/ # 4. 员工管理 │ ├── controller/ │ │ └── InvoiceEmployeeController.java │ ├── dto/ │ │ ├── EmployeeSaveDTO.java │ │ └── EmployeeVO.java │ ├── entity/ │ │ └── InvoiceEmployeeEntity.java │ ├── enums/ │ │ └── InvoiceEmployeeEnums.java (EmployeeRole) │ ├── mapper/ │ │ └── InvoiceEmployeeMapper.java │ └── service/ │ └── InvoiceEmployeeService.java ├── product/ # 5. 常用商品管理 │ ├── controller/ │ │ └── ProductController.java │ ├── dto/ │ │ ├── ProductSaveDTO.java │ │ └── ProductVO.java │ ├── entity/ │ │ └── ProductEntity.java │ ├── enums/ │ │ └── ProductEnums.java (ProductCategory) │ ├── mapper/ │ │ └── ProductMapper.java │ └── service/ │ └── ProductService.java ├── company/ # 6. 企业信息 │ ├── controller/ │ │ └── CompanyController.java │ ├── dto/ │ │ ├── CompanyVO.java │ │ ├── ContactUpdateDTO.java │ │ └── InvoiceInfoUpdateDTO.java │ ├── entity/ │ │ └── CompanyEntity.java │ ├── enums/ │ │ └── CompanyEnums.java (TaxCalcMethod, DefaultInvoiceType) │ ├── mapper/ │ │ └── CompanyMapper.java │ └── service/ │ └── CompanyService.java ├── supplier/ # 7. 供应商管理 │ ├── controller/ │ │ └── SupplierController.java │ ├── dto/ │ │ ├── SupplierSaveDTO.java │ │ ├── SupplierQueryDTO.java │ │ └── SupplierVO.java │ ├── entity/ │ │ └── SupplierEntity.java │ ├── enums/ │ │ └── SupplierEnums.java (AccountType, ConfirmStatus) │ ├── mapper/ │ │ └── SupplierMapper.java │ └── service/ │ └── SupplierService.java └── task/ # 8. 任务中心 ├── controller/ │ └── TaskController.java ├── dto/ │ ├── TaskQueryDTO.java │ └── TaskVO.java ├── entity/ │ └── TaskEntity.java ├── enums/ │ └── TaskEnums.java (TaskType, TaskStatus) ├── mapper/ │ └── TaskMapper.java ├── scheduler/ │ └── InvoiceTaskScheduler.java (poll export/import tasks, execute asynchronously) └── service/ ├── TaskService.java └── InvoiceMockDataService.java (singleton mock data factory for all modules) ``` **Key design decisions:** - Controllers: `@RestController` with `@RequestMapping("/payment/invoice/")` - Services: `@Service @RequiredArgsConstructor` using `LambdaQueryWrapper` for queries - Mappers: `@Mapper extends BaseMapper` - All entities extend `PaymentEnterpriseBaseEntity` (auto-tenant + auto-enterprise) - `InvoiceMockDataService` generates realistic mock data using Faker-like patterns for all modules - PageResult wrapping: `PageResult.of(pageNo, pageSize, total, items)` --- ## 3. API Endpoints (Mock Implementations) ### 3.1 Order Controller ``` GET /payment/invoice/order/list — paginated list with filters GET /payment/invoice/order/{id} — order detail GET /payment/invoice/order/invoice/{id} — invoice detail (红票/蓝票) POST /payment/invoice/order/batch-import — batch import (multipart .xlsx) POST /payment/invoice/order/batch-cancel — batch cancel by order IDs GET /payment/invoice/order/export/orders — export orders as .xlsx GET /payment/invoice/order/export/invoices — export invoices as .xlsx GET /payment/invoice/order/template — download import template ``` ### 3.2 Account Controller ``` GET /payment/invoice/account/balance — get account balance + pending amount GET /payment/invoice/account/recharge-info — get recharge bank account info POST /payment/invoice/account/recharge — (mock) recharge ``` ### 3.3 Tax Payment Controller ``` GET /payment/invoice/tax/config — get tax payment config (mode, proxy enabled) POST /payment/invoice/tax/mode — set payment mode (individual / enterprise) POST /payment/invoice/tax/enable-proxy — enable enterprise proxy payment GET /payment/invoice/tax/records — tax payment history ``` ### 3.4 Employee Controller ``` GET /payment/invoice/employee/list — employee list POST /payment/invoice/employee — add employee PUT /payment/invoice/employee/{id} — update employee DELETE /payment/invoice/employee/{id} — delete employee ``` ### 3.5 Product Controller ``` GET /payment/invoice/product/list — product list (filter by category) POST /payment/invoice/product — add product PUT /payment/invoice/product/{id} — update product DELETE /payment/invoice/product/{id} — delete product GET /payment/invoice/product/categories — category tree ``` ### 3.6 Company Controller ``` GET /payment/invoice/company/info — get company info (auto-create if missing) POST /payment/invoice/company/sync-tax — mock sync tax info POST /payment/invoice/company/verify — mock company verification PUT /payment/invoice/company/contact — update contact info PUT /payment/invoice/company/invoice-info — update invoice settings PUT /payment/invoice/company/trade-rules — update trade rule toggles ``` ### 3.7 Supplier Controller ``` GET /payment/invoice/supplier/list — paginated list with search filters POST /payment/invoice/supplier — add supplier PUT /payment/invoice/supplier/{id} — update supplier DELETE /payment/invoice/supplier/{id} — delete supplier POST /payment/invoice/supplier/batch-import — batch import (.xlsx) GET /payment/invoice/supplier/template — download import template ``` ### 3.8 Task Controller ``` GET /payment/invoice/task/list — paginated task list GET /payment/invoice/task/{id} — task detail GET /payment/invoice/task/{id}/download — download export file ``` ### Mock Data Strategy `InvoiceMockDataService` will be injected into each service constructor. It generates: - **Orders:** 50+ records with realistic Chinese names, amounts, statuses distributed across all 8 statuses - **Employees:** 5-10 records with masked phone/ID - **Products:** 20+ products distributed across all 9 categories - **Company:** 1 record per enterprise with hardcoded defaults (10M quota) - **Suppliers:** 15+ records with various account types and confirm statuses - **Tasks:** 10+ historical tasks with various types and statuses - **Account:** Fixed balance values Each service method checks if real data exists; if not, falls back to mock data. --- ## 4. Frontend Component Structure ``` src/views/module_payment/invoice/ ├── order/ │ ├── index.vue # main page with search + table │ ├── components/ │ │ ├── SearchForm.vue # search filters │ │ ├── OrderTable.vue # data table with actions │ │ ├── OrderDetailDialog.vue # order detail modal │ │ ├── InvoiceDetailDialog.vue # invoice detail (红票/蓝票) modal │ │ └── BatchImportDialog.vue # batch import with template download + upload │ └── index.ts # (if needed for local types) ├── account/ │ ├── index.vue # balance display + recharge section │ └── components/ │ ├── BalanceCard.vue # account balance + pending amount │ └── RechargeGuide.vue # collapsible "how to recharge" section ├── payTaxes/ │ ├── index.vue # tax payment mode selection │ └── components/ │ ├── IndividualPayment.vue # 个人缴税 view │ └── EnterpriseProxy.vue # 企业代缴 view with "立即开通" flow ├── employeeManagement/ │ ├── index.vue # employee list + add/edit dialogs │ └── components/ │ ├── EmployeeTable.vue # table with masked fields │ ├── AddEmployeeDialog.vue # role selector + dynamic form │ └── EditEmployeeDialog.vue # role readonly, edit form ├── goods/ │ ├── index.vue # left category tree + right product table │ └── components/ │ ├── CategoryTree.vue # 9-category scrapped product tree │ └── ProductTable.vue # product list by selected category ├── company/ │ ├── index.vue # enterprise info dashboard │ └── components/ │ ├── BasicInfoSection.vue # 8 read-only fields │ ├── ContactSection.vue # editable contact │ ├── CreditLimitSection.vue # 授信额度 4 fields │ ├── InvoiceInfoSection.vue # editable invoice settings │ ├── TradeRuleSection.vue # 4 toggle switches │ └── ServiceProviderSection.vue # 服务商授权管理 link ├── suppliers/ │ ├── index.vue # supplier list with search + batch import │ └── components/ │ ├── SupplierTable.vue # table with confirm_status filter │ ├── AddSupplierDialog.vue # add/edit with save+next │ └── BatchImportSupplier.vue # batch import dialog └── tasks/ ├── index.vue # task center with search └── components/ ├── TaskTable.vue # task list └── TaskDetailDrawer.vue # task detail side panel ``` API modules: ``` src/api/module_payment/invoice/ ├── order.ts ├── account.ts ├── tax.ts ├── employee.ts ├── product.ts ├── company.ts ├── supplier.ts └── task.ts ``` --- ## 5. Menu Tree Structure (sys_menu table) Insert SQL in a migration or seed file. Menu entries reference the dynamic component paths that the frontend `transformRoutes` resolves via `import.meta.glob("../../views/**/**.vue")`. ``` Industry Invoice Platform (parent menu) ├── type: 1 (directory), route_path: /invoice-platform, route_name: InvoicePlatform │ component_path: (empty, Layout handles it), icon: "invoice", order: 500 ├── 交易及发票管理 (type: 2 menu) │ parent_id: , route_path: /invoice-platform/order │ route_name: InvoiceOrder, component_path: module_payment/invoice/order/index │ permission: module_payment:invoice:order:list ├── 转账账户管理 │ parent_id: , route_path: /invoice-platform/account │ route_name: InvoiceAccount, component_path: module_payment/invoice/account/index │ permission: module_payment:invoice:account:view ├── 缴税管理 │ parent_id: , route_path: /invoice-platform/payTaxes │ route_name: InvoicePayTaxes, component_path: module_payment/invoice/payTaxes/index │ permission: module_payment:invoice:tax:view ├── 员工管理 │ parent_id: , route_path: /invoice-platform/employee │ route_name: InvoiceEmployee, component_path: module_payment/invoice/employeeManagement/index │ permission: module_payment:invoice:employee:list ├── 常用商品管理 │ parent_id: , route_path: /invoice-platform/goods │ route_name: InvoiceGoods, component_path: module_payment/invoice/goods/index │ permission: module_payment:invoice:product:list ├── 企业信息 │ parent_id: , route_path: /invoice-platform/company │ route_name: InvoiceCompany, component_path: module_payment/invoice/company/index │ permission: module_payment:invoice:company:view ├── 供应商管理 │ parent_id: , route_path: /invoice-platform/suppliers │ route_name: InvoiceSuppliers, component_path: module_payment/invoice/suppliers/index │ permission: module_payment:invoice:supplier:list └── 任务中心 parent_id: , route_path: /invoice-platform/tasks route_name: InvoiceTasks, component_path: module_payment/invoice/tasks/index permission: module_payment:invoice:task:list ``` **Important:** The `component_path` values must match the file structure `module_payment/invoice//index.vue`, because the permission store's `transformRoutes` resolves these with `modules[`../../views/${normalizedRoute.component}.vue`]`. --- ## 6. Implementation Order (7 phases) ### Phase 1: Database + Entities (Foundation) 1. Create `V1.2__create_pay_invoice_tables.sql` with all 7 tables + indexes 2. Run migration 3. Create all entity classes extending `PaymentEnterpriseBaseEntity` 4. Create all enum classes 5. Create all mapper interfaces ### Phase 2: Mock Service + Core Backend 1. Implement `InvoiceMockDataService` with mock data generation 2. Implement `CompanyService` — single-record per enterprise, auto-create on first access 3. Implement `OrderService` — list, detail, batch cancel, export 4. Implement `SupplierService` — list, CRUD, batch import 5. Implement `InvoiceEmployeeService` — CRUD 6. Implement `ProductService` — CRUD + categories 7. Implement `InvoiceAccountService` — balance query 8. Implement `TaxPaymentService` — config + history 9. Implement `TaskService` — list, detail ### Phase 3: Backend Controllers 1. Create all DTOs (VO + QueryDTO + SaveDTO) 2. Create all controllers with `@PreAuthorize` annotations matching menu permissions 3. Wire endpoints to services ### Phase 4: Frontend - API Layer + Foundation 1. Create `src/api/module_payment/invoice/` with all 8 API modules 2. Set up type definitions for request/response shapes ### Phase 5: Frontend - Simple Pages (bottom-up) Implement pages in order of complexity: 1. **任务中心** — simplest (table + filters, no CRUD) 2. **供应商管理** — simple CRUD table 3. **常用商品管理** — left tree + right table 4. **员工管理** — CRUD with dynamic form 5. **缴税管理** — radio toggle + button 6. **转账账户管理** — cards + collapsible section ### Phase 6: Frontend - Complex Pages 7. **企业信息** — multi-section dashboard with edit modes 8. **交易及发票管理** — most complex (search, table, batch ops, export, detail dialogs) ### Phase 7: Menu Config + Integration 1. Insert `sys_menu` records 2. Test navigation and route generation 3. Test tenant/enterprise scoping (verify mock data returns different records per tenant/enterprise) 4. Full integration smoke test --- ## 7. Critical Design Notes ### 7.1 Tenant Isolation All tables have `tenant_id` + `enterprise_id` columns. `TenantInnerInterceptor` auto-injects `tenant_id` on INSERT and auto-filters on SELECT. The `enterprise_id` must be set manually in the service layer when creating records (typically from the request context or current enterprise selection). **Mock data handling:** `InvoiceMockDataService` should accept `tenantId` and `enterpriseId` parameters so mock data is scoped correctly. Each enterprise sees only their own records. ### 7.2 Batch Import Flow 1. Frontend: download template (`GET /template` returns a pre-formatted .xlsx) 2. Frontend: upload file (`POST /batch-import` multipart) 3. Backend: parse .xlsx with Apache POI (already in project dependencies), validate rows, insert to DB 4. Create a task record in `pay_invoice_task` for tracking 5. Return success/fail counts ### 7.3 Export Flow 1. Frontend: click export triggers `GET /export/orders` or `GET /export/invoices` 2. Backend: generates .xlsx using `ExcelUtil`, saves to temp storage 3. Create a task record in `pay_invoice_task` with `task_status = 'SUCCESS'` 4. Return download URL in task detail ### 7.4 Scheduler (InvoiceTaskScheduler) A `@Scheduled` job that polls `pay_invoice_task` for `PENDING` tasks and processes them asynchronously. This handles: - Batch import parsing in background (for large files) - Export file generation - Cleanup of old/stale tasks ### 7.5 Frontend Multi-tenant The frontend already has an `enterprise.store.ts` for enterprise selection. All API calls should pass `enterprise_id` as a parameter (the backend's `PaymentEnterpriseBaseEntity` handles filtering). The current enterprise is available via `useEnterpriseStore()`. ### 7.6 Mock vs Real Switch Each service method should follow this pattern: ```java // In production, real API calls would go here // For now, return mock data return invoiceMockDataService.getOrders(tenantId, enterpriseId, queryDTO); ``` This makes future migration to real APIs straightforward — just replace the mock service call with a real API client call. ### 7.7 File Upload Size Limit Configure Spring multipart max size to 3MB for batch import (matches the Alipay constraint): ```yaml spring.servlet.multipart.max-file-size: 3MB spring.servlet.multipart.max-request-size: 3MB ``` But use a controller-level check to allow larger uploads elsewhere. --- ## 8. Files to Create (Total: ~65 files) ### Backend (~40 files) | Layer | Files | |---|---| | SQL Migration | 1 file | | Entities | 7 files | | Enums | 7 files | | Mappers | 8 files | | DTOs | ~15 files | | Services | 9 files (8 services + 1 mock service) | | Controllers | 8 files | | Scheduler | 1 file | ### Frontend (~25 files) | Layer | Files | |---|---| | API modules | 8 files | | View pages | 8 files | | Components | ~12 files | --- ## 9. Risks & Mitigations | Risk | Mitigation | |---|---| | Dynamic route resolution fails for new view paths | Verify `component_path` in sys_menu matches actual file path relative to `src/views/` | | Tenant interceptor not working for invoice tables | Tables use `tenant_id` column like existing `pay_*` tables — automatically picked up | | `enterprise_id` is String not Long (existing convention) | Use `String enterpriseId` consistently; `InvoiceMockDataService` uses String parameter | | Red/Blue invoice display not well-defined | Mock it: red invoice = negative amounts + red color; blue = normal | | Large batch import ~3MB causing timeout | Use task scheduler for async processing; immediate response with task ID | --- ## 10. Estimated Effort | Phase | Effort (hours) | |---|---| | Phase 1: DB + Entities | 3-4 | | Phase 2: Mock + Core Services | 6-8 | | Phase 3: Controllers + DTOs | 4-5 | | Phase 4: Frontend API Layer | 2-3 | | Phase 5: Simple Pages | 8-10 | | Phase 6: Complex Pages | 8-10 | | Phase 7: Menu + Integration | 2-4 | | **Total** | **33-44 hours** |