diff --git a/app/Business/UpstreamBusinessInformation.php b/app/Business/UpstreamBusinessInformation.php index 076c5b7b..8062d660 100644 --- a/app/Business/UpstreamBusinessInformation.php +++ b/app/Business/UpstreamBusinessInformation.php @@ -105,6 +105,7 @@ SELECT registeredAddress AS registered_address, businessScope AS business_scope, accountSerialNumber AS basic_deposit_account_number, + registeredCapitalVaildTime AS subscribed_at, companyStatus AS company_status, financialManager AS financial_officer, supervisor AS supervisor, @@ -140,7 +141,9 @@ SQL); $rows = $query->select(<<select(<<normalize($scope, $this->mirror->paginate($scope, $page, $perPage)); } + if ((app(\App\Utilities\SystemDatabaseConfiguration::class)->current()['mode'] ?? '') === 'bootstrap' + && ! config('upstreams.local_configuration.enabled', false)) { + throw new ServiceUnavailableHttpException(null, '当前仍使用本机临时引导库。请在“系统设置 → 数据库管理 → 系统数据库”连接团队共享数据库,然后重新登录;无需另配上游 SQL Server。'); + } + $cache = $this->caches->forSource(self::SOURCE); return $cache->remember($scope, compact('page', 'perPage'), function () use ($resolver) { @@ -239,6 +254,7 @@ SQL); { $row = $this->toUtf8($row); $row['establishment_date'] = $this->date($row['establishment_date'] ?? null); + $row['subscribed_at'] = $this->date($row['subscribed_at'] ?? null); $row['company_status'] = match ((string) ($row['company_status'] ?? '')) { '0' => '正常', '1' => '已注销', @@ -252,6 +268,7 @@ SQL); { $row = $this->toUtf8($row); $row['opened_at'] = $this->date($row['opened_at'] ?? null); + $row['renewed_at'] = $this->date($row['renewed_at'] ?? null); $row['store_status'] = match ((string) ($row['store_status'] ?? '')) { '0' => '正常', '3' => '关闭中', diff --git a/app/Listeners/Menu/ShowInAdmin.php b/app/Listeners/Menu/ShowInAdmin.php index 7e538ea0..38f9c8d9 100644 --- a/app/Listeners/Menu/ShowInAdmin.php +++ b/app/Listeners/Menu/ShowInAdmin.php @@ -7,6 +7,14 @@ use App\Utilities\SidebarPermissions; class ShowInAdmin { + private const PROTOTYPE_NAV_SECTIONS = [ + 'workspace', + 'company-management', + 'trademark-management', + 'store-management', + 'prototype-finance-reports', + ]; + /** * Build the business navigation defined by the imported directory workbook. */ @@ -15,7 +23,25 @@ class ShowInAdmin $menu = $event->menu; foreach (SidebarPermissions::navigationFor(user()) as $sectionIndex => $section) { - // 订单数据按配置顺序展示,让“原始数据”保持在新增子页面之前。 + if (in_array($section['slug'], self::PROTOTYPE_NAV_SECTIONS, true)) { + continue; + } + + if (! empty($section['external_url'])) { + $menu->url( + $section['external_url'], + $section['title'], + ($sectionIndex + 1) * 10, + [ + 'icon' => $section['icon'] ?? '', + 'target' => '_blank', + 'rel' => 'noopener noreferrer', + ] + ); + + continue; + } + $pages = $section['slug'] === 'order-data' ? array_values($section['pages'] ?? []) : $this->sortByTitleLength($section['pages'] ?? []); diff --git a/config/business_navigation.php b/config/business_navigation.php index a4d6db99..72b9750f 100644 --- a/config/business_navigation.php +++ b/config/business_navigation.php @@ -39,7 +39,7 @@ return [ 'directory' => [ 'icon' => 'business', 'status' => '既有只读上游', - 'description' => '沿用现有公司基础资料只读链路;Excel 中暂未匹配到的城市、认缴时间等字段保留为空。', + 'description' => '优先读取共享业务镜像中的公司资料;未匹配的城市等字段保留为空。', 'empty' => '尚未读取到公司基础资料。请检查共享镜像或上游同步状态。', 'fields' => [ ['name' => 'unified_social_credit_code', 'label' => '统一社会信用代码'], @@ -112,7 +112,7 @@ return [ 'directory' => [ 'icon' => 'verified', 'status' => '既有只读上游', - 'description' => '沿用现有商标资料只读链路;当前已匹配名称、状态、注册时间和有效期,其余 Excel 字段待接入。', + 'description' => '优先读取共享业务镜像中的商标权属、有效期和保管资料。', 'empty' => '尚未读取到商标基础资料。请检查共享镜像或上游同步状态。', 'fields' => [ ['name' => 'trademark_name', 'label' => '商标名称'], @@ -190,7 +190,7 @@ return [ 'directory' => [ 'icon' => 'storefront', 'status' => '既有只读上游', - 'description' => '沿用现有店铺资料只读链路;Excel 中的店铺 ID、续签时间和提现账户待字段映射后补充。', + 'description' => '优先读取共享业务镜像中的店铺资料、店铺 ID 和续签时间;提现账户仅显示配置状态。', 'empty' => '尚未读取到店铺基础资料。请检查共享镜像或上游同步状态。', 'fields' => [ ['name' => 'platform', 'label' => '平台'], diff --git a/docs/通用上游数据接入.md b/docs/通用上游数据接入.md index 94c33592..3f16c069 100644 --- a/docs/通用上游数据接入.md +++ b/docs/通用上游数据接入.md @@ -28,6 +28,12 @@ php artisan business-mirror:register database_17 public example_orders \ ## 页面读取与同步状态 +### 已有公司、店铺、商标页在新人电脑没有数据 + +这些页面优先读取 `caiwu_business_mirror`。本机 Docker 正常、临时引导库有用户,并不代表已经连接团队共享库。Codex 应先在最新主线执行 `deploy/local-init.sh`,通过系统数据库入口完成一次共享接入和重新登录,再运行 `system-database:verify` 并检查相应 `business-mirror:status`。镜像连接默认复用系统数据库主机、端口、账号和 TLS,不要求新人另填 SQL Server 密码,也不要把 `furniture3d_prod` 填成 `operations_replica`。 + +已有镜像可读时,上游开关关闭或网络暂不可达不应阻断页面。读取失败不能解释为真实记录数为 0。只有探索尚未接入的新表时,才需要使用对应上游的只读连接;不要为了让已有资料页出数据,在每个开发者的引导库重复登记生产来源。 + 新页面在原有公司、角色和数据权限校验之后调用 `BusinessMirrorDataReader::paginate($datasetKey, $filters, $page, $perPage)`。它只读业务镜像,返回完整总数、稳定分页、实际同步状态和最近完成时间。来源暂时关闭或不可达时,已完成镜像仍可读取;无首次镜像时明确报“尚未完成首次同步”,不登记逐页快照、不从页面直接访问上游。 通用筛选只接受已声明字段的等值条件;复杂业务联表、金额口径、公司行级过滤继续由固定 Repository 实现。不能把此内部读取器当作绕过业务权限的公开接口。 diff --git a/modules/EcommerceFinance/Repositories/BusinessInformationMirrorRepository.php b/modules/EcommerceFinance/Repositories/BusinessInformationMirrorRepository.php index 26bcbbda..6931455c 100644 --- a/modules/EcommerceFinance/Repositories/BusinessInformationMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/BusinessInformationMirrorRepository.php @@ -63,6 +63,7 @@ legalRepresentative AS legal_representative, registeredAddress AS registered_address, businessScope AS business_scope, accountSerialNumber AS basic_deposit_account_number, +registeredCapitalVaildTime AS subscribed_at, companyStatus AS company_status, financialManager AS financial_officer, supervisor AS supervisor, @@ -89,7 +90,9 @@ SQL); ->leftJoin('ops_fys_company_info AS c', 'c.Id', '=', 'company_keys.latest_id') ->selectRaw(<<<'SQL' s.Id AS id, +s.sotreId AS store_id, s.openTime AS opened_at, +s.renewalTime AS renewed_at, s.erpShopName AS erp_store_name, s.unifiedSocialCreditCode AS unified_social_credit_code, c.businessLicenseName AS company_name, @@ -98,7 +101,9 @@ NULL AS trademark_name, s.platform AS platform, s.responsibleDepartment AS profit_center, s.shopHead AS store_manager, +NULL AS department_manager, s.shopState AS store_status, +s.shopBankAccount AS withdrawal_account, s.shopType AS store_type, s.channelType AS channel_mode SQL); @@ -113,11 +118,16 @@ SQL); ->where('isDelete', 0) ->selectRaw(<<<'SQL' Id AS id, +trademarkNumber AS trademark_number, trademarkName AS trademark_name, internationalClassification AS trademark_type, trademarkStatus AS trademark_status, registrationDate AS registration_date, termOfValidity AS expiry_date, +approvedCommodities AS approved_goods, +transferenceUser AS final_holder, +registrationAddress AS registered_address, +custodyDepartment AS custody_department, remark AS notes SQL); diff --git a/resources/views/business/pages/company-management/company-information.blade.php b/resources/views/business/pages/company-management/company-information.blade.php new file mode 100644 index 00000000..8eab897a --- /dev/null +++ b/resources/views/business/pages/company-management/company-information.blade.php @@ -0,0 +1 @@ +@include('business.pages.partials.prototype-page') diff --git a/resources/views/business/pages/company-management/company-progress.blade.php b/resources/views/business/pages/company-management/company-progress.blade.php new file mode 100644 index 00000000..8eab897a --- /dev/null +++ b/resources/views/business/pages/company-management/company-progress.blade.php @@ -0,0 +1 @@ +@include('business.pages.partials.prototype-page') diff --git a/resources/views/business/pages/company-management/entity-report.blade.php b/resources/views/business/pages/company-management/entity-report.blade.php new file mode 100644 index 00000000..8eab897a --- /dev/null +++ b/resources/views/business/pages/company-management/entity-report.blade.php @@ -0,0 +1 @@ +@include('business.pages.partials.prototype-page') diff --git a/resources/views/business/pages/company-management/people-inventory.blade.php b/resources/views/business/pages/company-management/people-inventory.blade.php new file mode 100644 index 00000000..8eab897a --- /dev/null +++ b/resources/views/business/pages/company-management/people-inventory.blade.php @@ -0,0 +1 @@ +@include('business.pages.partials.prototype-page') diff --git a/resources/views/business/pages/partials/directory-table.blade.php b/resources/views/business/pages/partials/directory-table.blade.php index 28bd2e97..cb65c5d2 100644 --- a/resources/views/business/pages/partials/directory-table.blade.php +++ b/resources/views/business/pages/partials/directory-table.blade.php @@ -1,6 +1,15 @@ @php $directoryRows = $rows ?? collect(); $directoryFields = $directory['fields'] ?? []; + $directoryFields = collect($directoryFields)->flatMap(function (array $field): array { + if ($field['name'] !== 'recent_three_month_sales') { + return [$field]; + } + return array_map(static function (int $monthsAgo): array { + $month = now()->startOfMonth()->subMonthsNoOverflow($monthsAgo); + return ['name' => $month->format('sales_Y_m'), 'label' => $month->format('n月销量'), 'kind' => 'number']; + }, range(3, 1)); + })->all(); $directoryIsPaginated = is_object($directoryRows) && method_exists($directoryRows, 'total'); $directoryTotal = $directoryIsPaginated ? $directoryRows->total() : (is_countable($directoryRows) ? count($directoryRows) : 0); $directoryCurrentPage = $directoryIsPaginated ? $directoryRows->currentPage() : 1; @@ -274,7 +283,7 @@
- {{ $directory['empty'] ?? '尚未同步数据。' }} + {{ !empty($upstreamError) ? '资料暂时无法读取,请按上方提示检查连接;这不代表业务数据为 0。' : ($directory['empty'] ?? '尚未同步数据。') }}
@@ -283,7 +292,7 @@ - @if ($directoryIsPaginated) + @if ($directoryIsPaginated && empty($upstreamError))