已添加15个文件
已删除4个文件
已修改18个文件
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | {"t":0,"agent":"system","event":"skill_invoked","skill_name":"superpowers:brainstorming"} |
| | | {"t":0,"agent":"a8f801a","agent_type":"code-reviewer","event":"agent_start","parent_mode":"none"} |
| | | {"t":0,"agent":"a8f801a","agent_type":"code-reviewer","event":"agent_stop","success":true,"duration_ms":50944} |
| | | {"t":0,"agent":"a51d0d4","agent_type":"code-reviewer","event":"agent_start","parent_mode":"none"} |
| | | {"t":0,"agent":"a51d0d4","agent_type":"code-reviewer","event":"agent_stop","success":true,"duration_ms":27507} |
| | | {"t":0,"agent":"a05bbbd","agent_type":"code-reviewer","event":"agent_start","parent_mode":"none"} |
| | | {"t":0,"agent":"a05bbbd","agent_type":"code-reviewer","event":"agent_stop","success":true,"duration_ms":9125} |
| | | {"t":0,"agent":"system","event":"skill_invoked","skill_name":"superpowers:writing-plans"} |
| | | {"t":0,"agent":"a8e49fa","agent_type":"code-reviewer","event":"agent_start","parent_mode":"none"} |
| | | {"t":0,"agent":"a8e49fa","agent_type":"code-reviewer","event":"agent_stop","success":true,"duration_ms":31352} |
| | | {"t":0,"agent":"adb1029","agent_type":"code-reviewer","event":"agent_start","parent_mode":"none"} |
| | | {"t":0,"agent":"adb1029","agent_type":"code-reviewer","event":"agent_stop","success":true,"duration_ms":9005} |
| | | {"t":0,"agent":"system","event":"skill_invoked","skill_name":"superpowers:subagent-driven-development"} |
| | | {"t":0,"agent":"system","event":"skill_invoked","skill_name":"superpowers:using-git-worktrees"} |
| | |
| | | { |
| | | "lastSentAt": "2026-04-12T12:35:52.350Z" |
| | | "lastSentAt": "2026-04-13T06:33:17.906Z" |
| | | } |
| | |
| | | { |
| | | "tool_name": "Read", |
| | | "tool_input_preview": "{\"file_path\":\"D:\\\\Git\\\\ShanMeiXinNengYuan\\\\Code\\\\WMS\\\\WIDESEA_WMSServer\\\\WIDESEA_WMSServer\\\\Controllers\\\\BatteryCellController.cs\"}", |
| | | "error": "File does not exist. Note: your current working directory is D:\\Git\\ShanMeiXinNengYuan\\Code.", |
| | | "timestamp": "2026-04-12T13:48:48.578Z", |
| | | "retry_count": 1 |
| | | "tool_name": "Bash", |
| | | "tool_input_preview": "{\"command\":\"ls \\\"D:\\\\Git\\\\ShanMeiXinNengYuan\\\\Code\\\\WMS\\\\WIDESEA_WMSClient\\\\src\\\\api\\\\\\\" 2>/dev/null | head -20\",\"description\":\"Check api directory structure\"}", |
| | | "error": "Exit code 2\n/usr/bin/bash: eval: line 1: unexpected EOF while looking for matching `\"'", |
| | | "timestamp": "2026-04-13T03:27:59.200Z", |
| | | "retry_count": 6 |
| | | } |
| | |
| | | { |
| | | "updatedAt": "2026-04-12T13:54:45.455Z", |
| | | "updatedAt": "2026-04-13T02:47:35.467Z", |
| | | "missions": [ |
| | | { |
| | | "id": "session:9007b9ea-1eb6-4d24-8fe7-2c3a949eac88:none", |
| | |
| | | "sourceKey": "session-stop:a0ff37729a29ea04f" |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | "id": "session:e24754b1-493a-48fb-9409-1ba4fbbf8fb3:none", |
| | | "source": "session", |
| | | "name": "none", |
| | | "objective": "Session mission", |
| | | "createdAt": "2026-04-13T02:37:40.066Z", |
| | | "updatedAt": "2026-04-13T02:47:35.467Z", |
| | | "status": "done", |
| | | "workerCount": 5, |
| | | "taskCounts": { |
| | | "total": 5, |
| | | "pending": 0, |
| | | "blocked": 0, |
| | | "inProgress": 0, |
| | | "completed": 5, |
| | | "failed": 0 |
| | | }, |
| | | "agents": [ |
| | | { |
| | | "name": "code-reviewer:a8f801a", |
| | | "role": "code-reviewer", |
| | | "ownership": "a8f801a79ff5dda6f", |
| | | "status": "done", |
| | | "currentStep": null, |
| | | "latestUpdate": "completed", |
| | | "completedSummary": null, |
| | | "updatedAt": "2026-04-13T02:38:31.010Z" |
| | | }, |
| | | { |
| | | "name": "code-reviewer:a51d0d4", |
| | | "role": "code-reviewer", |
| | | "ownership": "a51d0d447d3482f88", |
| | | "status": "done", |
| | | "currentStep": null, |
| | | "latestUpdate": "completed", |
| | | "completedSummary": null, |
| | | "updatedAt": "2026-04-13T02:40:32.731Z" |
| | | }, |
| | | { |
| | | "name": "code-reviewer:a05bbbd", |
| | | "role": "code-reviewer", |
| | | "ownership": "a05bbbd6e272317db", |
| | | "status": "done", |
| | | "currentStep": null, |
| | | "latestUpdate": "completed", |
| | | "completedSummary": null, |
| | | "updatedAt": "2026-04-13T02:41:24.944Z" |
| | | }, |
| | | { |
| | | "name": "code-reviewer:a8e49fa", |
| | | "role": "code-reviewer", |
| | | "ownership": "a8e49fa8974f48c7e", |
| | | "status": "done", |
| | | "currentStep": null, |
| | | "latestUpdate": "completed", |
| | | "completedSummary": null, |
| | | "updatedAt": "2026-04-13T02:44:54.211Z" |
| | | }, |
| | | { |
| | | "name": "code-reviewer:adb1029", |
| | | "role": "code-reviewer", |
| | | "ownership": "adb1029c24100db60", |
| | | "status": "done", |
| | | "currentStep": null, |
| | | "latestUpdate": "completed", |
| | | "completedSummary": null, |
| | | "updatedAt": "2026-04-13T02:47:35.467Z" |
| | | } |
| | | ], |
| | | "timeline": [ |
| | | { |
| | | "id": "session-start:a8e49fa8974f48c7e:2026-04-13T02:44:22.859Z", |
| | | "at": "2026-04-13T02:44:22.859Z", |
| | | "kind": "update", |
| | | "agent": "code-reviewer:a8e49fa", |
| | | "detail": "started code-reviewer:a8e49fa", |
| | | "sourceKey": "session-start:a8e49fa8974f48c7e" |
| | | }, |
| | | { |
| | | "id": "session-stop:a8e49fa8974f48c7e:2026-04-13T02:44:54.211Z", |
| | | "at": "2026-04-13T02:44:54.211Z", |
| | | "kind": "completion", |
| | | "agent": "code-reviewer:a8e49fa", |
| | | "detail": "completed", |
| | | "sourceKey": "session-stop:a8e49fa8974f48c7e" |
| | | }, |
| | | { |
| | | "id": "session-start:adb1029c24100db60:2026-04-13T02:47:26.462Z", |
| | | "at": "2026-04-13T02:47:26.462Z", |
| | | "kind": "update", |
| | | "agent": "code-reviewer:adb1029", |
| | | "detail": "started code-reviewer:adb1029", |
| | | "sourceKey": "session-start:adb1029c24100db60" |
| | | }, |
| | | { |
| | | "id": "session-stop:adb1029c24100db60:2026-04-13T02:47:35.467Z", |
| | | "at": "2026-04-13T02:47:35.467Z", |
| | | "kind": "completion", |
| | | "agent": "code-reviewer:adb1029", |
| | | "detail": "completed", |
| | | "sourceKey": "session-stop:adb1029c24100db60" |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | } |
| | |
| | | "status": "completed", |
| | | "completed_at": "2026-04-12T13:54:45.455Z", |
| | | "duration_ms": 7226 |
| | | }, |
| | | { |
| | | "agent_id": "a8f801a79ff5dda6f", |
| | | "agent_type": "code-reviewer", |
| | | "started_at": "2026-04-13T02:37:40.066Z", |
| | | "parent_mode": "none", |
| | | "status": "completed", |
| | | "completed_at": "2026-04-13T02:38:31.010Z", |
| | | "duration_ms": 50944 |
| | | }, |
| | | { |
| | | "agent_id": "a51d0d447d3482f88", |
| | | "agent_type": "code-reviewer", |
| | | "started_at": "2026-04-13T02:40:05.224Z", |
| | | "parent_mode": "none", |
| | | "status": "completed", |
| | | "completed_at": "2026-04-13T02:40:32.731Z", |
| | | "duration_ms": 27507 |
| | | }, |
| | | { |
| | | "agent_id": "a05bbbd6e272317db", |
| | | "agent_type": "code-reviewer", |
| | | "started_at": "2026-04-13T02:41:15.819Z", |
| | | "parent_mode": "none", |
| | | "status": "completed", |
| | | "completed_at": "2026-04-13T02:41:24.944Z", |
| | | "duration_ms": 9125 |
| | | }, |
| | | { |
| | | "agent_id": "a8e49fa8974f48c7e", |
| | | "agent_type": "code-reviewer", |
| | | "started_at": "2026-04-13T02:44:22.859Z", |
| | | "parent_mode": "none", |
| | | "status": "completed", |
| | | "completed_at": "2026-04-13T02:44:54.211Z", |
| | | "duration_ms": 31352 |
| | | }, |
| | | { |
| | | "agent_id": "adb1029c24100db60", |
| | | "agent_type": "code-reviewer", |
| | | "started_at": "2026-04-13T02:47:26.462Z", |
| | | "parent_mode": "none", |
| | | "status": "completed", |
| | | "completed_at": "2026-04-13T02:47:35.467Z", |
| | | "duration_ms": 9005 |
| | | } |
| | | ], |
| | | "total_spawned": 27, |
| | | "total_completed": 27, |
| | | "total_spawned": 32, |
| | | "total_completed": 32, |
| | | "total_failed": 0, |
| | | "last_updated": "2026-04-12T13:54:45.571Z" |
| | | "last_updated": "2026-04-13T02:47:35.573Z" |
| | | } |
| | |
| | | <PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" /> |
| | | <PackageReference Include="Magicodes.IE.EPPlus" Version="2.7.5.1" /> |
| | | <PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.1" /> |
| | | <PackageReference Include="Masuit.Tools.Core" Version="2026.1.3" /> |
| | | <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.29" /> |
| | | <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> |
| | | <PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.3.8" /> |
| | | <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> |
| | | <PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> |
| | | <PackageReference Include="OfficeOpenXml.Core.ExcelPackage" Version="1.0.0" /> |
| | | <PackageReference Include="Serilog" Version="4.3.1" /> |
| | | <PackageReference Include="Serilog.AspNetCore" Version="6.0.0" /> |
| | |
| | | |
| | | <ItemGroup> |
| | | <PackageReference Include="StackExchange.Redis" Version="2.7.33" /> |
| | | <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> |
| | | <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" /> |
| | | <PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> |
| | | <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.5" /> |
| | | <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" /> |
| | | <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" /> |
| | | </ItemGroup> |
| | |
| | | continue; |
| | | } |
| | | |
| | | #region æ£æµæ¯å¦éè¦ç©ºæç |
| | | |
| | | // ========== æ£æ¥ç¹å®ä½ç½®æ¯å¦ææç ========== |
| | | // ä»é
ç½®ä¸è¯»åéè¦æ£æ¥æççä½ç½®å表 |
| | |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | // ========== æ£æ¥ PLC_STB æ å¿ ========== |
| | | // åªæå½ PLC_STB 为 1 æ¶æå¤çä»»å¡ |
| | | if (command.PLC_STB != 1) |
| | |
| | | "vue-draggable-next": "^2.0.1", |
| | | "vue-qrcode": "^2.2.2", |
| | | "vue-router": "^4.0.0-0", |
| | | "vue3-json-viewer": "^2.4.1", |
| | | "vuex": "^4.0.0-0" |
| | | }, |
| | | "devDependencies": { |
| | |
| | | vue-router: |
| | | specifier: ^4.0.0-0 |
| | | version: 4.6.4(vue@3.5.30) |
| | | vue3-json-viewer: |
| | | specifier: ^2.4.1 |
| | | version: 2.4.1(vue@3.5.30) |
| | | vuex: |
| | | specifier: ^4.0.0-0 |
| | | version: 4.1.0(vue@3.5.30) |
| | |
| | | resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} |
| | | engines: {node: '>=6.0'} |
| | | |
| | | clipboard@2.0.11: |
| | | resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==} |
| | | |
| | | cliui@6.0.0: |
| | | resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} |
| | | |
| | |
| | | default-user-agent@1.0.0: |
| | | resolution: {integrity: sha512-bDF7bg6OSNcSwFWPu4zYKpVkJZQYVrAANMYB8bc9Szem1D0yKdm4sa/rOCs2aC9+2GMqQ7KnwtZRvDhmLF0dXw==} |
| | | engines: {node: '>= 0.10.0'} |
| | | |
| | | delegate@3.2.0: |
| | | resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} |
| | | |
| | | destroy@1.2.0: |
| | | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} |
| | |
| | | glob@7.2.3: |
| | | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} |
| | | deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me |
| | | |
| | | good-listener@1.2.2: |
| | | resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==} |
| | | |
| | | gopd@1.2.0: |
| | | resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} |
| | |
| | | sdk-base@2.0.1: |
| | | resolution: {integrity: sha512-eeG26wRwhtwYuKGCDM3LixCaxY27Pa/5lK4rLKhQa7HBjJ3U3Y+f81MMZQRsDw/8SC2Dao/83yJTXJ8aULuN8Q==} |
| | | |
| | | select@1.1.2: |
| | | resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==} |
| | | |
| | | semver@5.7.2: |
| | | resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} |
| | | hasBin: true |
| | |
| | | through@2.3.8: |
| | | resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} |
| | | |
| | | tiny-emitter@2.1.0: |
| | | resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} |
| | | |
| | | to-arraybuffer@1.0.1: |
| | | resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} |
| | | |
| | |
| | | resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} |
| | | peerDependencies: |
| | | vue: ^3.5.0 |
| | | |
| | | vue3-json-viewer@2.4.1: |
| | | resolution: {integrity: sha512-Z1sunvS58lJ3ZcpNhl3jYQapBVw2wjnXbemigfMWm3QnjCeg3CPMq8R6pxHUYahxMfPKLvrbGve6mUXqhWyLaQ==} |
| | | peerDependencies: |
| | | vue: ^3.5.16 |
| | | |
| | | vue@3.5.30: |
| | | resolution: {integrity: sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==} |
| | |
| | | |
| | | chrome-trace-event@1.0.4: {} |
| | | |
| | | clipboard@2.0.11: |
| | | dependencies: |
| | | good-listener: 1.2.2 |
| | | select: 1.1.2 |
| | | tiny-emitter: 2.1.0 |
| | | |
| | | cliui@6.0.0: |
| | | dependencies: |
| | | string-width: 4.2.3 |
| | |
| | | default-user-agent@1.0.0: |
| | | dependencies: |
| | | os-name: 1.0.3 |
| | | |
| | | delegate@3.2.0: {} |
| | | |
| | | destroy@1.2.0: {} |
| | | |
| | |
| | | minimatch: 3.1.5 |
| | | once: 1.4.0 |
| | | path-is-absolute: 1.0.1 |
| | | |
| | | good-listener@1.2.2: |
| | | dependencies: |
| | | delegate: 3.2.0 |
| | | |
| | | gopd@1.2.0: {} |
| | | |
| | |
| | | dependencies: |
| | | get-ready: 1.0.0 |
| | | |
| | | select@1.1.2: {} |
| | | |
| | | semver@5.7.2: {} |
| | | |
| | | semver@6.3.1: {} |
| | |
| | | |
| | | through@2.3.8: {} |
| | | |
| | | tiny-emitter@2.1.0: {} |
| | | |
| | | to-arraybuffer@1.0.1: {} |
| | | |
| | | tough-cookie@4.1.4: |
| | |
| | | '@vue/devtools-api': 6.6.4 |
| | | vue: 3.5.30 |
| | | |
| | | vue3-json-viewer@2.4.1(vue@3.5.30): |
| | | dependencies: |
| | | clipboard: 2.0.11 |
| | | vue: 3.5.30 |
| | | |
| | | vue@3.5.30: |
| | | dependencies: |
| | | '@vue/compiler-dom': 3.5.30 |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="mes-log-statistics"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-primary"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">æ»è°ç¨æ¬¡æ°</div> |
| | | <div class="stat-value">{{ statistics.totalCount }}</div> |
| | | <div class="stat-unit">次</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card" :class="statistics.successRate >= 90 ? 'stat-success' : 'stat-warning'"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">æåç</div> |
| | | <div class="stat-value">{{ statistics.successRate }}%</div> |
| | | <div class="stat-sub"> |
| | | æå: {{ statistics.successCount }} / 失败: {{ statistics.failedCount }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-info"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">å¹³åèæ¶</div> |
| | | <div class="stat-value">{{ Math.round(statistics.avgElapsedMs) }}</div> |
| | | <div class="stat-unit">ms</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-secondary"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">仿¥è°ç¨</div> |
| | | <div class="stat-value">{{ statistics.todayCount }}</div> |
| | | <div class="stat-unit">次</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ref, onMounted } from "vue"; |
| | | import http from "@/api/http.js"; |
| | | |
| | | export default { |
| | | name: "MesLogStatistics", |
| | | emits: ["refresh"], |
| | | setup(props, { emit }) { |
| | | const statistics = ref({ |
| | | totalCount: 0, |
| | | successCount: 0, |
| | | failedCount: 0, |
| | | successRate: 0, |
| | | avgElapsedMs: 0, |
| | | todayCount: 0 |
| | | }); |
| | | |
| | | const fetchStatistics = async () => { |
| | | try { |
| | | const res = await http.get("/api/MesLog/statistics"); |
| | | if (res.status) { |
| | | statistics.value = res.data; |
| | | emit("refresh"); |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åç»è®¡æ°æ®å¤±è´¥:", error); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchStatistics(); |
| | | }); |
| | | |
| | | return { |
| | | statistics, |
| | | fetchStatistics |
| | | }; |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .mes-log-statistics { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .stat-card { |
| | | text-align: center; |
| | | } |
| | | |
| | | .stat-content { |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 28px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .stat-sub { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .stat-primary .stat-value { color: #409EFF; } |
| | | .stat-success .stat-value { color: #67C23A; } |
| | | .stat-warning .stat-value { color: #E6A23C; } |
| | | .stat-info .stat-value { color: #909399; } |
| | | .stat-secondary .stat-value { color: #909399; } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { h, createApp } from 'vue'; |
| | | import { ElDrawer, ElIcon } from 'element-plus'; |
| | | |
| | | let extension = { |
| | | components: { |
| | | // 卿æ©å
ç»ä»¶æç»ä»¶è·¯å¾ |
| | | gridHeader: "", |
| | | gridBody: '', |
| | | gridFooter: "", |
| | | modelHeader: "", |
| | | modelBody: "", |
| | | modelFooter: "" |
| | | }, |
| | | buttons: [], // æ©å±çæé® |
| | | data: { |
| | | jsonDrawerVisible: false, |
| | | currentJson: '', |
| | | currentJsonTitle: '' |
| | | }, |
| | | methods: { |
| | | // äºä»¶æ©å± |
| | | onInit() { |
| | | console.log("mes_log init"); |
| | | this.setFiexdSearchForm(true); |
| | | }, |
| | | |
| | | onInited() { |
| | | this.height = this.height - 240; // 为ç»è®¡å¡çé¢çç©ºé´ |
| | | |
| | | // æ·»å é¢è§æ¹æ³ |
| | | this.previewJson = (jsonStr) => { |
| | | if (!jsonStr) return '-'; |
| | | try { |
| | | const obj = JSON.parse(jsonStr); |
| | | return JSON.stringify(obj, null, 2).substring(0, 200) + '...'; |
| | | } catch { |
| | | return String(jsonStr).substring(0, 200) + '...'; |
| | | } |
| | | }; |
| | | }, |
| | | |
| | | // è¡ç¹å»äºä»¶ - æ¾ç¤º JSON 详æ
|
| | | rowClick({ row, column }) { |
| | | // 妿ç¹å»çæ¯è¯·æ±æååºåï¼æ¾ç¤ºè¯¦æ
æ½å± |
| | | if (column.property === 'requestJson' && row.requestJson) { |
| | | this.showJsonDetail(row, 'request'); |
| | | } else if (column.property === 'responseJson' && row.responseJson) { |
| | | this.showJsonDetail(row, 'response'); |
| | | } |
| | | }, |
| | | |
| | | // æ¾ç¤º JSON 详æ
æ½å± |
| | | showJsonDetail(row, type = 'request') { |
| | | const jsonContent = type === 'request' ? row.requestJson : row.responseJson; |
| | | const title = type === 'request' ? 'ð è¯·æ± JSON' : 'ð¥ ååº JSON'; |
| | | |
| | | // æ ¼å¼å JSON |
| | | let formattedJson = ''; |
| | | try { |
| | | const obj = typeof jsonContent === 'string' ? JSON.parse(jsonContent) : jsonContent; |
| | | formattedJson = JSON.stringify(obj, null, 2); |
| | | } catch (e) { |
| | | formattedJson = String(jsonContent); |
| | | } |
| | | |
| | | // å建临æ¶å®¹å¨æ¸²ææ½å± |
| | | const container = document.createElement('div'); |
| | | document.body.appendChild(container); |
| | | |
| | | const app = createApp({ |
| | | render() { |
| | | return h('div', [ |
| | | h(ElDrawer, { |
| | | modelValue: true, |
| | | 'onUpdate:modelValue': (val) => { |
| | | if (!val) { |
| | | app.unmount(); |
| | | document.body.removeChild(container); |
| | | } |
| | | }, |
| | | title: title, |
| | | size: '30%', |
| | | destroyOnClose: true, |
| | | closeOnClickModal: true |
| | | }, { |
| | | default: () => h('div', { |
| | | style: { |
| | | height: '100%', |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '16px', |
| | | borderRadius: '4px' |
| | | } |
| | | }, [ |
| | | h('pre', { |
| | | style: { |
| | | margin: '0', |
| | | fontSize: '14px', |
| | | lineHeight: '1.5', |
| | | fontFamily: 'Consolas, Monaco, "Courier New", monospace', |
| | | whiteSpace: 'pre-wrap', |
| | | wordBreak: 'break-all' |
| | | } |
| | | }, formattedJson) |
| | | ]) |
| | | }) |
| | | ]); |
| | | } |
| | | }); |
| | | |
| | | app.use(window.ElementPlus); |
| | | app.mount(container); |
| | | }, |
| | | } |
| | | }; |
| | | |
| | | export default extension; |
| | |
| | | import { h, resolveComponent } from 'vue'; |
| | | import { h, createApp } from 'vue'; |
| | | import { ElDrawer } from 'element-plus'; |
| | | |
| | | let extension = { |
| | | components: { |
| | | //卿æ©å
ç»ä»¶æç»ä»¶è·¯å¾ |
| | |
| | | }, |
| | | onInited() { |
| | | this.height = this.height - 170; |
| | | }, |
| | | |
| | | // è¡ç¹å»äºä»¶ - æ¾ç¤ºåæ°è¯¦æ
æ½å± |
| | | rowClick({ row, column }) { |
| | | // 妿ç¹å»çæ¯è¯·æ±åæ°æååºåæ°åï¼æ¾ç¤ºè¯¦æ
æ½å± |
| | | if (column.property === 'requestParam' && row.requestParam) { |
| | | this.showJsonDetail(row, 'request'); |
| | | } else if (column.property === 'responseParam' && row.responseParam) { |
| | | this.showJsonDetail(row, 'response'); |
| | | } |
| | | }, |
| | | |
| | | // æ¾ç¤º JSON 详æ
æ½å± |
| | | showJsonDetail(row, type = 'request') { |
| | | const content = type === 'request' ? row.requestParam : row.responseParam; |
| | | const title = type === 'request' ? 'ð 请æ±åæ°' : 'ð¥ ååºåæ°'; |
| | | |
| | | // æ ¼å¼å JSON |
| | | let formattedJson = ''; |
| | | try { |
| | | const obj = typeof content === 'string' ? JSON.parse(content) : content; |
| | | formattedJson = JSON.stringify(obj, null, 2); |
| | | } catch (e) { |
| | | formattedJson = String(content); |
| | | } |
| | | |
| | | // å建临æ¶å®¹å¨æ¸²ææ½å± |
| | | const container = document.createElement('div'); |
| | | document.body.appendChild(container); |
| | | |
| | | const app = createApp({ |
| | | render() { |
| | | return h('div', [ |
| | | h(ElDrawer, { |
| | | modelValue: true, |
| | | 'onUpdate:modelValue': (val) => { |
| | | if (!val) { |
| | | app.unmount(); |
| | | document.body.removeChild(container); |
| | | } |
| | | }, |
| | | title: title, |
| | | size: '30%', |
| | | destroyOnClose: true, |
| | | closeOnClickModal: true |
| | | }, { |
| | | default: () => h('div', { |
| | | style: { |
| | | height: '30%', |
| | | backgroundColor: '#f5f5f5', |
| | | padding: '16px', |
| | | borderRadius: '4px' |
| | | } |
| | | }, [ |
| | | h('pre', { |
| | | style: { |
| | | margin: '0', |
| | | fontSize: '14px', |
| | | lineHeight: '1.5', |
| | | fontFamily: 'Consolas, Monaco, "Courier New", monospace', |
| | | whiteSpace: 'pre-wrap', |
| | | wordBreak: 'break-all' |
| | | } |
| | | }, formattedJson) |
| | | ]) |
| | | }) |
| | | ]); |
| | | } |
| | | }); |
| | | |
| | | app.use(window.ElementPlus); |
| | | app.mount(container); |
| | | } |
| | | } |
| | | }; |
| | |
| | | component: () => import('@/views/system/Sys_Log.vue') |
| | | }, |
| | | { |
| | | path: '/Mes_Log', |
| | | name: 'mes_Log', |
| | | component: () => import('@/views/system/Mes_Log.vue') |
| | | }, |
| | | { |
| | | path: '/Sys_User', |
| | | name: 'Sys_User', |
| | | component: () => import('@/views/system/Sys_User.vue') |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <!-- |
| | | *Authorï¼System |
| | | *Contactï¼- |
| | | *代ç ç±æ¡æ¶çæ,任使´æ¹é½å¯è½å¯¼è´è¢«ä»£ç çæå¨è¦ç |
| | | *ä¸å¡è¯·å¨@/extension/system/Mes_Log.jsxæ¤å¤ç¼å |
| | | --> |
| | | <template> |
| | | <div class="mes-log-page"> |
| | | <!-- ç»è®¡å¡çåºå --> |
| | | <mes-log-statistics ref="statistics" /> |
| | | |
| | | <!-- æ¥å¿å表 --> |
| | | <view-grid |
| | | ref="grid" |
| | | :columns="columns" |
| | | :detail="detail" |
| | | :editFormFields="editFormFields" |
| | | :editFormOptions="editFormOptions" |
| | | :searchFormFields="searchFormFields" |
| | | :searchFormOptions="searchFormOptions" |
| | | :table="table" |
| | | :extend="extend" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import extend from "@/extension/system/Mes_Log.jsx"; |
| | | import MesLogStatistics from "@/components/MesLogStatistics.vue"; |
| | | import { ref, defineComponent } from "vue"; |
| | | |
| | | export default defineComponent({ |
| | | name: "Mes_Log", |
| | | components: { |
| | | MesLogStatistics |
| | | }, |
| | | setup() { |
| | | const table = ref({ |
| | | key: "Id", |
| | | cnName: "MESæ¥å£æ¥å¿", |
| | | name: "Mes_Log", |
| | | url: "/MesLog/", |
| | | sortName: "Id" |
| | | }); |
| | | |
| | | const columns = ref([ |
| | | { field: "id", title: "ID", width: 80, hidden: true }, |
| | | { |
| | | field: "apiType", |
| | | title: "æ¥å£ç±»å", |
| | | width: 130, |
| | | bind: { key: "mesApiType", data: [] } |
| | | }, |
| | | { |
| | | field: "isSuccess", |
| | | title: "ç¶æ", |
| | | width: 80, |
| | | bind: { key: "mesApiStatus", data: [] } |
| | | }, |
| | | { |
| | | field: "requestJson", |
| | | title: "请æ±å
容", |
| | | width: 200, |
| | | formatter: (row) => { |
| | | if (!row.requestJson) return '-'; |
| | | const preview = row.requestJson.length > 50 |
| | | ? row.requestJson.substring(0, 50) + '...' |
| | | : row.requestJson; |
| | | return `<span style="color: #409EFF; cursor: pointer;">${preview}</span>`; |
| | | } |
| | | }, |
| | | { |
| | | field: "responseJson", |
| | | title: "ååºå
容", |
| | | width: 200, |
| | | formatter: (row) => { |
| | | if (!row.responseJson) return '-'; |
| | | const preview = row.responseJson.length > 50 |
| | | ? row.responseJson.substring(0, 50) + '...' |
| | | : row.responseJson; |
| | | return `<span style="color: #409EFF; cursor: pointer;">${preview}</span>`; |
| | | } |
| | | }, |
| | | { field: "errorMessage", title: "é误信æ¯", width: 250 }, |
| | | { field: "elapsedMs", title: "èæ¶(ms)", width: 100, sortable: true }, |
| | | { field: "createDate", title: "è°ç¨æ¶é´", width: 160, sortable: true }, |
| | | { field: "creator", title: "æä½äºº", width: 100 } |
| | | ]); |
| | | |
| | | const detail = ref({ |
| | | cnName: "MESæ¥å¿è¯¦æ
", |
| | | columns: [], |
| | | sortName: "Id", |
| | | key: "Id" |
| | | }); |
| | | |
| | | const editFormFields = ref({}); |
| | | const editFormOptions = ref([]); |
| | | |
| | | const searchFormFields = ref({ |
| | | apiType: "", |
| | | isSuccess: "", |
| | | dateRange: "", |
| | | creator: "", |
| | | elapsedRange: "", |
| | | errorKeyword: "", |
| | | jsonKeyword: "" |
| | | }); |
| | | |
| | | const searchFormOptions = ref([ |
| | | [ |
| | | { field: "apiType", title: "æ¥å£ç±»å", type: "select",dataKey: "mesApiType", data: [] }, |
| | | { field: "isSuccess", title: "ç¶æ", type: "select" ,dataKey: "mesApiStatus", data: []}, |
| | | { field: "dateRange", title: "æ¶é´èå´", type: "datetimeRange" } |
| | | ], |
| | | [ |
| | | { field: "creator", title: "æä½äºº", type: "text" }, |
| | | { |
| | | field: "elapsedRange", |
| | | title: "èæ¶èå´(ms)", |
| | | type: "numberRange", |
| | | placeholder: ["æå°", "æå¤§"] |
| | | } |
| | | ], |
| | | [ |
| | | { field: "errorKeyword", title: "é误å
³é®å", type: "text" }, |
| | | { field: "jsonKeyword", title: "JSONå
容å
³é®å", type: "text" } |
| | | ] |
| | | ]); |
| | | |
| | | return { |
| | | table, |
| | | columns, |
| | | detail, |
| | | editFormFields, |
| | | editFormOptions, |
| | | searchFormFields, |
| | | searchFormOptions, |
| | | extend |
| | | }; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .mes-log-page { |
| | | padding: 16px; |
| | | } |
| | | </style> |
| | |
| | | field: "requestParam", |
| | | title: "请æ±åæ°", |
| | | type: "string", |
| | | width: 100, |
| | | width: 150, |
| | | align: "left", |
| | | formatter: (row) => { |
| | | if (!row.requestParam) return '-'; |
| | | const preview = row.requestParam.length > 50 |
| | | ? row.requestParam.substring(0, 50) + '...' |
| | | : row.requestParam; |
| | | return `<span style="color: #409EFF; cursor: pointer;">${preview}</span>`; |
| | | } |
| | | }, |
| | | { |
| | | field: "responseParam", |
| | | title: "ååºåæ°", |
| | | type: "string", |
| | | width: 100, |
| | | width: 150, |
| | | align: "left", |
| | | formatter: (row) => { |
| | | if (!row.responseParam) return '-'; |
| | | const preview = row.responseParam.length > 50 |
| | | ? row.responseParam.substring(0, 50) + '...' |
| | | : row.responseParam; |
| | | return `<span style="color: #409EFF; cursor: pointer;">${preview}</span>`; |
| | | } |
| | | }, |
| | | { |
| | | field: "url", |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================= |
| | | -- MES æ¥å£æ¥å¿é¡µé¢æ°æ®åºé
ç½®èæ¬ |
| | | -- åå»ºæ¥æ: 2026-04-13 |
| | | -- 说æ: å建æ§è½ç´¢å¼ãèåè®°å½ãæ°æ®åå
¸ |
| | | -- ============================================= |
| | | |
| | | -- ============================================= |
| | | -- Step 1: å建æ§è½ç´¢å¼ |
| | | -- ============================================= |
| | | |
| | | -- æ¥å£ç±»åç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_ApiType' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_ApiType ON Dt_MesApiLog(ApiType); |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_ApiType å·²å建'; |
| | | END |
| | | ELSE |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_ApiType å·²åå¨'; |
| | | |
| | | -- å建æ¶é´ç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_CreateDate' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_CreateDate ON Dt_MesApiLog(CreateDate DESC); |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_CreateDate å·²å建'; |
| | | END |
| | | ELSE |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_CreateDate å·²åå¨'; |
| | | |
| | | -- æåç¶æç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_IsSuccess' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_IsSuccess ON Dt_MesApiLog(IsSuccess); |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_IsSuccess å·²å建'; |
| | | END |
| | | ELSE |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_IsSuccess å·²åå¨'; |
| | | |
| | | -- åå»ºäººç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_Creator' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_Creator ON Dt_MesApiLog(Creator); |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_Creator å·²å建'; |
| | | END |
| | | ELSE |
| | | PRINT 'ç´¢å¼ IX_MesApiLog_Creator å·²åå¨'; |
| | | |
| | | PRINT '====================================='; |
| | | PRINT 'ç´¢å¼åå»ºå®æ'; |
| | | PRINT '====================================='; |
| | | GO |
| | | |
| | | -- ============================================= |
| | | -- Step 2: æ·»å èåè®°å½ |
| | | -- ============================================= |
| | | |
| | | -- æ¥è¯¢ç³»ç»ç®¡çèåç ID |
| | | DECLARE @SystemMenuId INT; |
| | | SELECT TOP 1 @SystemMenuId = Id FROM Dt_Menu WHERE MenuName LIKE '%ç³»ç»%' OR MenuName LIKE '%System%'; |
| | | |
| | | IF @SystemMenuId IS NULL |
| | | BEGIN |
| | | PRINT 'è¦å: æªæ¾å°ç³»ç»ç®¡çèåï¼è¯·æå¨ç¡®è®¤èå ID'; |
| | | END |
| | | ELSE |
| | | BEGIN |
| | | PRINT 'ç³»ç»ç®¡çèå ID: ' + CAST(@SystemMenuId AS VARCHAR(10)); |
| | | |
| | | -- æ£æ¥ MES æ¥å£æ¥å¿è忝å¦å·²åå¨ |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_Menu WHERE Url = '/Mes_Log') |
| | | BEGIN |
| | | INSERT INTO Dt_Menu (ParentId, MenuName, Url, Component, Permission, Sort, Icon, CreateDate, Modifier) |
| | | VALUES ( |
| | | @SystemMenuId, |
| | | 'MESæ¥å£æ¥å¿', |
| | | '/Mes_Log', |
| | | 'views/system/Mes_Log', |
| | | 'Mes_Log:view', |
| | | (SELECT ISNULL(MAX(Sort), 0) + 1 FROM Dt_Menu WHERE ParentId = @SystemMenuId), |
| | | 'el-icon-document', |
| | | GETDATE(), |
| | | 'System' |
| | | ); |
| | | PRINT 'MESæ¥å£æ¥å¿èå已添å ï¼ID: ' + CAST(SCOPE_IDENTITY() AS VARCHAR(10)); |
| | | END |
| | | ELSE |
| | | BEGIN |
| | | PRINT 'MESæ¥å£æ¥å¿èåå·²åå¨'; |
| | | END |
| | | END |
| | | GO |
| | | |
| | | -- ============================================= |
| | | -- Step 3: æ·»å æ°æ®åå
¸è®°å½ |
| | | -- ============================================= |
| | | |
| | | -- æ¥å£ç±»ååå
¸ |
| | | DECLARE @DictId INT; |
| | | SELECT @DictId = Id FROM Dt_Dictionary WHERE DictKey = 'mesApiType'; |
| | | |
| | | IF @DictId IS NULL |
| | | BEGIN |
| | | INSERT INTO Dt_Dictionary (DictKey, DictValue, CreateDate, Modifier) |
| | | VALUES ('mesApiType', 'MESæ¥å£ç±»å', GETDATE(), 'System'); |
| | | SET @DictId = SCOPE_IDENTITY(); |
| | | PRINT 'MESæ¥å£ç±»ååå
¸å·²å建ï¼ID: ' + CAST(@DictId AS VARCHAR(10)); |
| | | END |
| | | ELSE |
| | | BEGIN |
| | | PRINT 'MESæ¥å£ç±»ååå
¸å·²åå¨ï¼ID: ' + CAST(@DictId AS VARCHAR(10)); |
| | | END |
| | | |
| | | -- æ·»å æ¥å£ç±»åé项 |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'BindContainer') |
| | | BEGIN |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, [Key], DisplayOrder, CreateDate, Modifier) |
| | | VALUES (@DictId, 'çµè¯ç»å®', 'BindContainer', 1, GETDATE(), 'System'); |
| | | PRINT 'æ¥å£ç±»åé项已添å : BindContainer (çµè¯ç»å®)'; |
| | | END |
| | | |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'UnBindContainer') |
| | | BEGIN |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, [Key], DisplayOrder, CreateDate, Modifier) |
| | | VALUES (@DictId, 'çµè¯è§£ç»', 'UnBindContainer', 2, GETDATE(), 'System'); |
| | | PRINT 'æ¥å£ç±»åé项已添å : UnBindContainer (çµè¯è§£ç»)'; |
| | | END |
| | | |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'ContainerNgReport') |
| | | BEGIN |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, [Key], DisplayOrder, CreateDate, Modifier) |
| | | VALUES (@DictId, 'NG䏿¥', 'ContainerNgReport', 3, GETDATE(), 'System'); |
| | | PRINT 'æ¥å£ç±»åé项已添å : ContainerNgReport (NG䏿¥)'; |
| | | END |
| | | |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'InboundInContainer') |
| | | BEGIN |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, [Key], DisplayOrder, CreateDate, Modifier) |
| | | VALUES (@DictId, 'æçè¿ç«', 'InboundInContainer', 4, GETDATE(), 'System'); |
| | | PRINT 'æ¥å£ç±»åé项已添å : InboundInContainer (æçè¿ç«)'; |
| | | END |
| | | |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'OutboundInContainer') |
| | | BEGIN |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, [Key], DisplayOrder, CreateDate, Modifier) |
| | | VALUES (@DictId, 'æçåºç«', 'OutboundInContainer', 5, GETDATE(), 'System'); |
| | | PRINT 'æ¥å£ç±»åé项已添å : OutboundInContainer (æçåºç«)'; |
| | | END |
| | | |
| | | -- è°ç¨ç¶æåå
¸ |
| | | DECLARE @StatusDictId INT; |
| | | SELECT @StatusDictId = Id FROM Dt_Dictionary WHERE DictKey = 'mesApiStatus'; |
| | | |
| | | IF @StatusDictId IS NULL |
| | | BEGIN |
| | | INSERT INTO Dt_Dictionary (DictKey, DictValue, CreateDate, Modifier) |
| | | VALUES ('mesApiStatus', 'MESæ¥å£ç¶æ', GETDATE(), 'System'); |
| | | SET @StatusDictId = SCOPE_IDENTITY(); |
| | | PRINT 'MESæ¥å£ç¶æåå
¸å·²å建ï¼ID: ' + CAST(@StatusDictId AS VARCHAR(10)); |
| | | END |
| | | ELSE |
| | | BEGIN |
| | | PRINT 'MESæ¥å£ç¶æåå
¸å·²åå¨ï¼ID: ' + CAST(@StatusDictId AS VARCHAR(10)); |
| | | END |
| | | |
| | | -- æ·»å ç¶æé项 |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @StatusDictId AND [Key] = 'true') |
| | | BEGIN |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, [Key], DisplayOrder, CreateDate, Modifier) |
| | | VALUES (@StatusDictId, 'æå', 'true', 1, GETDATE(), 'System'); |
| | | PRINT 'ç¶æé项已添å : true (æå)'; |
| | | END |
| | | |
| | | IF NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @StatusDictId AND [Key] = 'false') |
| | | BEGIN |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, [Key], DisplayOrder, CreateDate, Modifier) |
| | | VALUES (@StatusDictId, '失败', 'false', 2, GETDATE(), 'System'); |
| | | PRINT 'ç¶æé项已添å : false (失败)'; |
| | | END |
| | | |
| | | PRINT '====================================='; |
| | | PRINT 'æ°æ®åå
¸é
ç½®å®æ'; |
| | | PRINT '====================================='; |
| | | GO |
| | | |
| | | -- ============================================= |
| | | -- æ§è¡å®æ¯ |
| | | -- ============================================= |
| | | PRINT '====================================='; |
| | | PRINT 'MES æ¥å£æ¥å¿é¡µé¢æ°æ®åºé
ç½®æ§è¡å®æ¯'; |
| | | PRINT '====================================='; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.IO; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using SqlSugar; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_BasicService |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å£æ¥å¿æå¡å®ç° |
| | | /// </summary> |
| | | public class MesLogService : ServiceBase<Dt_MesApiLog, IRepository<Dt_MesApiLog>>, IMesLogService |
| | | { |
| | | private readonly ISqlSugarClient _db; |
| | | |
| | | /// <summary> |
| | | /// æé 彿° |
| | | /// </summary> |
| | | /// <param name="db">æ°æ®åºå®¢æ·ç«¯</param> |
| | | public MesLogService(ISqlSugarClient db, IRepository<Dt_MesApiLog> repository) : base(repository) |
| | | { |
| | | _db = db; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è®°å½MESæ¥å£è°ç¨æ¥å¿ |
| | | /// </summary> |
| | | /// <param name="log">æ¥å¿DTO</param> |
| | | /// <returns>æ¯å¦è®°å½æå</returns> |
| | | public async Task<bool> LogAsync(MesApiLogDto log) |
| | | { |
| | | try |
| | | { |
| | | var entity = new Dt_MesApiLog |
| | | { |
| | | ApiType = log.ApiType, |
| | | RequestJson = log.RequestJson, |
| | | ResponseJson = log.ResponseJson, |
| | | IsSuccess = log.IsSuccess, |
| | | ErrorMessage = log.ErrorMessage, |
| | | ElapsedMs = log.ElapsedMs, |
| | | CreateDate = DateTime.Now, |
| | | Creator = log.Creator |
| | | }; |
| | | |
| | | var result = await _db.Insertable(entity).ExecuteCommandAsync(); |
| | | return result > 0; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // TODO: 使ç¨é¡¹ç®æ¥å¿æ¡æ¶è®°å½é误 |
| | | Console.WriteLine($"è®°å½MESæ¥å¿å¤±è´¥: {ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæè¿çMESæ¥å£è°ç¨è®°å½ |
| | | /// </summary> |
| | | /// <param name="apiType">æ¥å£ç±»å</param> |
| | | /// <param name="count">è®°å½æ°é</param> |
| | | /// <returns>æ¥å¿å表</returns> |
| | | public async Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50) |
| | | { |
| | | try |
| | | { |
| | | var entities = await _db.Queryable<Dt_MesApiLog>() |
| | | .Where(x => x.ApiType == apiType) |
| | | .OrderByDescending(x => x.CreateDate) |
| | | .Take(count) |
| | | .ToListAsync(); |
| | | |
| | | return entities.Select(e => new MesApiLogDto |
| | | { |
| | | ApiType = e.ApiType, |
| | | RequestJson = e.RequestJson, |
| | | ResponseJson = e.ResponseJson, |
| | | IsSuccess = e.IsSuccess, |
| | | ErrorMessage = e.ErrorMessage, |
| | | ElapsedMs = e.ElapsedMs, |
| | | Creator = e.Creator |
| | | }).ToList(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // TODO: 使ç¨é¡¹ç®æ¥å¿æ¡æ¶è®°å½é误 |
| | | Console.WriteLine($"è·åMESæ¥å¿å¤±è´¥: {ex.Message}"); |
| | | return new List<MesApiLogDto>(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | public async Task<(List<MesLogListItemDto> items, int total)> GetPageAsync(MesLogQueryDto query, int page, int pageSize) |
| | | { |
| | | var dbQuery = _db.Queryable<Dt_MesApiLog>(); |
| | | |
| | | // åºç¨ç鿡件 |
| | | if (!string.IsNullOrEmpty(query.ApiType)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ApiType == query.ApiType); |
| | | } |
| | | |
| | | if (query.IsSuccess.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.IsSuccess == query.IsSuccess.Value); |
| | | } |
| | | |
| | | if (query.StartTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate >= query.StartTime.Value); |
| | | } |
| | | |
| | | if (query.EndTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate <= query.EndTime.Value); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.Creator)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.Creator != null && x.Creator.Contains(query.Creator)); |
| | | } |
| | | |
| | | if (query.MinElapsedMs.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ElapsedMs >= query.MinElapsedMs.Value); |
| | | } |
| | | |
| | | if (query.MaxElapsedMs.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ElapsedMs <= query.MaxElapsedMs.Value); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.ErrorKeyword)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ErrorMessage != null && x.ErrorMessage.Contains(query.ErrorKeyword)); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.JsonRequestKeyword)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.RequestJson != null && x.RequestJson.Contains(query.JsonRequestKeyword)); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.JsonResponseKeyword)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ResponseJson != null && x.ResponseJson.Contains(query.JsonResponseKeyword)); |
| | | } |
| | | |
| | | // è·åæ»æ° |
| | | var total = await dbQuery.CountAsync(); |
| | | |
| | | // å页æ¥è¯¢ |
| | | var entities = await dbQuery |
| | | .OrderByDescending(x => x.CreateDate) |
| | | .ToPageListAsync(page, pageSize); |
| | | |
| | | var items = entities.Select(e => new MesLogListItemDto |
| | | { |
| | | Id = e.Id, |
| | | ApiType = e.ApiType, |
| | | IsSuccess = e.IsSuccess, |
| | | RequestJson = e.RequestJson?.Length > 200 ? e.RequestJson.Substring(0, 200) + "..." : e.RequestJson, |
| | | ResponseJson = e.ResponseJson?.Length > 200 ? e.ResponseJson.Substring(0, 200) + "..." : e.ResponseJson, |
| | | ErrorMessage = e.ErrorMessage, |
| | | ElapsedMs = e.ElapsedMs, |
| | | CreateDate = e.CreateDate, |
| | | Creator = e.Creator |
| | | }).ToList(); |
| | | |
| | | return (items, total); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·ååæ¡æ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | public async Task<MesLogDetailDto?> GetDetailAsync(long id) |
| | | { |
| | | var entity = await _db.Queryable<Dt_MesApiLog>() |
| | | .Where(x => x.Id == id) |
| | | .FirstAsync(); |
| | | |
| | | if (entity == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | return new MesLogDetailDto |
| | | { |
| | | Id = entity.Id, |
| | | ApiType = entity.ApiType, |
| | | IsSuccess = entity.IsSuccess, |
| | | RequestJson = entity.RequestJson, |
| | | ResponseJson = entity.ResponseJson, |
| | | RequestJsonPreview = entity.RequestJson?.Length > 200 ? entity.RequestJson.Substring(0, 200) + "..." : entity.RequestJson, |
| | | ResponseJsonPreview = entity.ResponseJson?.Length > 200 ? entity.ResponseJson.Substring(0, 200) + "..." : entity.ResponseJson, |
| | | ErrorMessage = entity.ErrorMessage, |
| | | ElapsedMs = entity.ElapsedMs, |
| | | CreateDate = entity.CreateDate, |
| | | Creator = entity.Creator, |
| | | ModifyDate = entity.ModifyDate, |
| | | Modifier = entity.Modifier |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | public async Task<MesLogStatisticsDto> GetStatisticsAsync(MesLogQueryDto query) |
| | | { |
| | | var dbQuery = _db.Queryable<Dt_MesApiLog>(); |
| | | |
| | | // åºç¨ç鿡件 |
| | | if (!string.IsNullOrEmpty(query.ApiType)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ApiType == query.ApiType); |
| | | } |
| | | |
| | | if (query.IsSuccess.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.IsSuccess == query.IsSuccess.Value); |
| | | } |
| | | |
| | | if (query.StartTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate >= query.StartTime.Value); |
| | | } |
| | | |
| | | if (query.EndTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate <= query.EndTime.Value); |
| | | } |
| | | |
| | | // ä½¿ç¨æ°æ®åºèå计ç®ç»è®¡æ°æ® |
| | | var totalCount = await dbQuery.CountAsync(); |
| | | var successCount = await dbQuery.Where(x => x.IsSuccess).CountAsync(); |
| | | var failedCount = totalCount - successCount; |
| | | var successRate = totalCount > 0 ? (successCount * 100.0 / totalCount) : 0; |
| | | |
| | | // è·åæææ°æ®æ¥è®¡ç®å¹³åå¼åæå¤§å¼ï¼SqlSugar éå¶ï¼ |
| | | var allData = await dbQuery.Select(x => x.ElapsedMs).ToListAsync(); |
| | | var avgElapsed = allData.Count > 0 ? allData.Average() : 0; |
| | | var maxElapsed = allData.Count > 0 ? allData.Max() : 0; |
| | | |
| | | // 仿¥æ°æ® |
| | | var today = DateTime.Today; |
| | | var todayCount = await dbQuery.Where(x => x.CreateDate >= today).CountAsync(); |
| | | |
| | | return new MesLogStatisticsDto |
| | | { |
| | | TotalCount = totalCount, |
| | | SuccessCount = successCount, |
| | | FailedCount = failedCount, |
| | | SuccessRate = Math.Round(successRate, 2), |
| | | AvgElapsedMs = avgElapsed, |
| | | MaxElapsedMs = maxElapsed, |
| | | TodayCount = todayCount |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿æ°æ® |
| | | /// </summary> |
| | | public async Task<byte[]> ExportAsync(MesLogQueryDto query) |
| | | { |
| | | // è·åææç¬¦åæ¡ä»¶çæ°æ®ï¼éå¶50000è¡ï¼ |
| | | var (items, _) = await GetPageAsync(query, 1, 50000); |
| | | |
| | | using var memoryStream = new MemoryStream(); |
| | | using var writer = new StreamWriter(memoryStream, Encoding.UTF8); |
| | | |
| | | // åå
¥ UTF-8 BOMï¼ä½¿ Excel è½æ£ç¡®è¯å«ä¸æ |
| | | writer.Write('\uFEFF'); |
| | | |
| | | // CSV å¤´é¨ |
| | | writer.WriteLine("ID,æ¥å£ç±»å,ç¶æ,èæ¶(ms),é误信æ¯,å建æ¶é´,å建人"); |
| | | |
| | | // CSV æ°æ®è¡ |
| | | foreach (var item in items) |
| | | { |
| | | var error = item.ErrorMessage?.Replace("\"", "\"\"") ?? ""; |
| | | var status = item.IsSuccess ? "æå" : "失败"; |
| | | writer.WriteLine($"{item.Id},{item.ApiType},{status},{item.ElapsedMs},\"{error}\",{item.CreateDate:yyyy-MM-dd HH:mm:ss},{item.Creator ?? ""}"); |
| | | } |
| | | |
| | | writer.Flush(); |
| | | return memoryStream.ToArray(); |
| | | } |
| | | } |
| | | } |
| | |
| | | /// <summary> |
| | | /// MESæå¡å®ç° - é西顷å»è½æºç§æMESç³»ç»å¯¹æ¥ |
| | | /// </summary> |
| | | public class MesService : IMesService |
| | | public class MesService : IMesService |
| | | { |
| | | private readonly HttpClientHelper _httpClient; |
| | | private readonly string _baseUrl; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿è¯¦æ
DTO |
| | | /// </summary> |
| | | public class MesLogDetailDto |
| | | { |
| | | /// <summary> |
| | | /// æ¥å¿ID |
| | | /// </summary> |
| | | public long Id { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¥å£ç±»å |
| | | /// </summary> |
| | | public string ApiType { get; set; } = string.Empty; |
| | | |
| | | /// <summary> |
| | | /// æ¯å¦æå |
| | | /// </summary> |
| | | public bool IsSuccess { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±JSONï¼å®æ´ï¼ |
| | | /// </summary> |
| | | public string? RequestJson { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºJSONï¼å®æ´ï¼ |
| | | /// </summary> |
| | | public string? ResponseJson { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±JSONé¢è§ |
| | | /// </summary> |
| | | public string? RequestJsonPreview { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºJSONé¢è§ |
| | | /// </summary> |
| | | public string? ResponseJsonPreview { get; set; } |
| | | |
| | | /// <summary> |
| | | /// éè¯¯ä¿¡æ¯ |
| | | /// </summary> |
| | | public string? ErrorMessage { get; set; } |
| | | |
| | | /// <summary> |
| | | /// èæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public int ElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å建æ¶é´ |
| | | /// </summary> |
| | | public DateTime CreateDate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å建人 |
| | | /// </summary> |
| | | public string? Creator { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¿®æ¹æ¶é´ |
| | | /// </summary> |
| | | public DateTime? ModifyDate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¿®æ¹äºº |
| | | /// </summary> |
| | | public string? Modifier { get; set; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿å表项DTO |
| | | /// </summary> |
| | | public class MesLogListItemDto |
| | | { |
| | | /// <summary> |
| | | /// æ¥å¿ID |
| | | /// </summary> |
| | | public long Id { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¥å£ç±»å |
| | | /// </summary> |
| | | public string ApiType { get; set; } = string.Empty; |
| | | |
| | | /// <summary> |
| | | /// æ¯å¦æå |
| | | /// </summary> |
| | | public bool IsSuccess { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±JSONï¼é¢è§ï¼ |
| | | /// </summary> |
| | | public string? RequestJson { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºJSONï¼é¢è§ï¼ |
| | | /// </summary> |
| | | public string? ResponseJson { get; set; } |
| | | |
| | | /// <summary> |
| | | /// éè¯¯ä¿¡æ¯ |
| | | /// </summary> |
| | | public string? ErrorMessage { get; set; } |
| | | |
| | | /// <summary> |
| | | /// èæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public int ElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å建æ¶é´ |
| | | /// </summary> |
| | | public DateTime CreateDate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å建人 |
| | | /// </summary> |
| | | public string? Creator { get; set; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿æ¥è¯¢è¯·æ±DTO |
| | | /// </summary> |
| | | public class MesLogQueryDto |
| | | { |
| | | /// <summary> |
| | | /// æ¥å£ç±»å: BindContainer, UnBindContainer, ContainerNgReport, InboundInContainer, OutboundInContainer |
| | | /// </summary> |
| | | public string? ApiType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåç¶æ: null-å
¨é¨, true-æå, false-失败 |
| | | /// </summary> |
| | | public bool? IsSuccess { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¼å§æ¶é´ï¼å端 dateRange åæ®µæ å°ï¼dateRange[0] â StartTimeï¼ |
| | | /// </summary> |
| | | public DateTime? StartTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç»ææ¶é´ï¼å端 dateRange åæ®µæ å°ï¼dateRange[1] â EndTimeï¼ |
| | | /// </summary> |
| | | public DateTime? EndTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åå»ºäººï¼æä½äººï¼ |
| | | /// </summary> |
| | | public string? Creator { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå°èæ¶ï¼æ¯«ç§ï¼ï¼å端 elapsedRange åæ®µæ å°ï¼elapsedRange[0] â MinElapsedMsï¼ |
| | | /// </summary> |
| | | public int? MinElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§èæ¶ï¼æ¯«ç§ï¼ï¼å端 elapsedRange åæ®µæ å°ï¼elapsedRange[1] â MaxElapsedMsï¼ |
| | | /// </summary> |
| | | public int? MaxElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é误信æ¯å
³é®å |
| | | /// </summary> |
| | | public string? ErrorKeyword { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±JSONå
³é®å |
| | | /// </summary> |
| | | public string? JsonRequestKeyword { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºJSONå
³é®å |
| | | /// </summary> |
| | | public string? JsonResponseKeyword { get; set; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Collections.Generic; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿ç»è®¡æ°æ®DTO |
| | | /// </summary> |
| | | public class MesLogStatisticsDto |
| | | { |
| | | /// <summary> |
| | | /// æ»è°ç¨æ¬¡æ° |
| | | /// </summary> |
| | | public int TotalCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåæ¬¡æ° |
| | | /// </summary> |
| | | public int SuccessCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¤±è´¥æ¬¡æ° |
| | | /// </summary> |
| | | public int FailedCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåçï¼ç¾åæ¯ï¼ |
| | | /// </summary> |
| | | public double SuccessRate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¹³åèæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public double AvgElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§èæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public int MaxElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 仿¥è°ç¨æ¬¡æ° |
| | | /// </summary> |
| | | public int TodayCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 忥å£ç±»åè°ç¨æ¬¡æ° |
| | | /// </summary> |
| | | public Dictionary<string, int>? ApiTypeCounts { get; set; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Collections.Generic; |
| | | using System.Threading.Tasks; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_IBasicService |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å£æ¥å¿æå¡æ¥å£ |
| | | /// </summary> |
| | | public interface IMesLogService : IService<Dt_MesApiLog> |
| | | { |
| | | /// <summary> |
| | | /// è®°å½MESæ¥å£è°ç¨æ¥å¿ |
| | | /// </summary> |
| | | /// <param name="log">æ¥å¿DTO</param> |
| | | /// <returns>æ¯å¦è®°å½æå</returns> |
| | | Task<bool> LogAsync(MesApiLogDto log); |
| | | |
| | | /// <summary> |
| | | /// è·åæè¿çMESæ¥å£è°ç¨è®°å½ |
| | | /// </summary> |
| | | /// <param name="apiType">æ¥å£ç±»å</param> |
| | | /// <param name="count">è®°å½æ°é</param> |
| | | /// <returns>æ¥å¿å表</returns> |
| | | Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50); |
| | | |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <param name="page">页ç </param> |
| | | /// <param name="pageSize">æ¯é¡µæ°é</param> |
| | | /// <returns>æ¥å¿åè¡¨åæ»æ°</returns> |
| | | Task<(List<MesLogListItemDto> items, int total)> GetPageAsync(MesLogQueryDto query, int page, int pageSize); |
| | | |
| | | /// <summary> |
| | | /// è·ååæ¡æ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | /// <param name="id">æ¥å¿ID</param> |
| | | /// <returns>æ¥å¿è¯¦æ
</returns> |
| | | Task<MesLogDetailDto?> GetDetailAsync(long id); |
| | | |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <returns>ç»è®¡æ°æ®</returns> |
| | | Task<MesLogStatisticsDto> GetStatisticsAsync(MesLogQueryDto query); |
| | | |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿æ°æ® |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <returns>CSVåèæ°ç»</returns> |
| | | Task<byte[]> ExportAsync(MesLogQueryDto query); |
| | | } |
| | | } |
| | |
| | | using SqlSugar; |
| | | using System; |
| | | |
| | | namespace WIDESEA_Model.Models.Mes |
| | | namespace WIDESEA_Model.Models |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å£è°ç¨æ¥å¿å®ä½ |
| | |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Common.WareHouseEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | // è°ç¨MESæçè¿ç« |
| | | //var inboundRequest = new InboundInContainerRequest |
| | | //{ |
| | | // EquipmentCode = "STK-GROUP-001", |
| | | // ResourceCode = "STK-GROUP-001", |
| | | // LocalTime = DateTime.Now, |
| | | // ContainerCode = taskDto.PalletCode |
| | | //}; |
| | | //var inboundResult = _mesService.InboundInContainer(inboundRequest); |
| | | //if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess) |
| | | //{ |
| | | // return content.Error($"ä»»å¡å®æå¤±è´¥ï¼MESè¿ç«å¤±è´¥: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | //} |
| | | var inboundRequest = new InboundInContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = taskDto.PalletCode |
| | | }; |
| | | var inboundResult = _mesService.InboundInContainer(inboundRequest); |
| | | if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"ä»»å¡å®æå¤±è´¥ï¼MESè¿ç«å¤±è´¥: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | return await CompleteTaskAsync(task, "å
¥åºå®æ"); |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using Autofac.Core; |
| | | using Microsoft.AspNetCore.Authorization; |
| | | using Microsoft.AspNetCore.Mvc; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseController; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_WMSServer.Controllers.Mes |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å£æ¥å¿æ§å¶å¨ |
| | | /// </summary> |
| | | [Route("api/MesLog")] |
| | | [ApiController] |
| | | [Authorize] |
| | | public class MesLogController : ApiBaseController<IMesLogService, Dt_MesApiLog> |
| | | { |
| | | private readonly IMesLogService _mesLogService; |
| | | |
| | | /// <summary> |
| | | /// æé 彿° |
| | | /// </summary> |
| | | public MesLogController(IMesLogService mesLogService) : base(mesLogService) |
| | | { |
| | | _mesLogService = mesLogService; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <param name="page">页ç ï¼é»è®¤1</param> |
| | | /// <param name="pageSize">æ¯é¡µæ°éï¼é»è®¤20</param> |
| | | /// <returns>åé¡µç»æ</returns> |
| | | [HttpPost("page")] |
| | | public async Task<WebResponseContent> GetPage([FromBody] MesLogQueryDto query, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) |
| | | { |
| | | var response = new WebResponseContent(); |
| | | try |
| | | { |
| | | var (items, total) = await _mesLogService.GetPageAsync(query, page, pageSize); |
| | | return response.OK(null, new { rows = items, total = total }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return response.Error($"æ¥è¯¢å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | /// <param name="id">æ¥å¿ID</param> |
| | | /// <returns>æ¥å¿è¯¦æ
</returns> |
| | | [HttpGet("{id}")] |
| | | public async Task<WebResponseContent> GetDetail(long id) |
| | | { |
| | | var response = new WebResponseContent(); |
| | | try |
| | | { |
| | | var detail = await _mesLogService.GetDetailAsync(id); |
| | | if (detail == null) |
| | | { |
| | | return response.Error("æ¥å¿ä¸åå¨"); |
| | | } |
| | | return response.OK(null, detail); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return response.Error($"æ¥è¯¢å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶ï¼å¯éï¼</param> |
| | | /// <returns>ç»è®¡æ°æ®</returns> |
| | | [HttpGet("statistics")] |
| | | public async Task<WebResponseContent> GetStatistics([FromQuery] MesLogQueryDto query) |
| | | { |
| | | var response = new WebResponseContent(); |
| | | try |
| | | { |
| | | var statistics = await _mesLogService.GetStatisticsAsync(query ?? new MesLogQueryDto()); |
| | | return response.OK(null, statistics); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return response.Error($"æ¥è¯¢å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿ |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <returns>CSVæä»¶</returns> |
| | | [HttpPost("export")] |
| | | public async Task<IActionResult> Export([FromBody] MesLogQueryDto query) |
| | | { |
| | | try |
| | | { |
| | | var data = await _mesLogService.ExportAsync(query ?? new MesLogQueryDto()); |
| | | var fileName = $"MESæ¥å£æ¥å¿_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; |
| | | return File(data, "text/csv; charset=utf-8", fileName); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return BadRequest(new { error = ex.Message }); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IMesService; |
| | | using WIDESEA_ISystemService; |
| | | using WIDESEA_Model.Models; |
| | | using WIDESEA_Common.StockEnum; |
| | |
| | | return response.Error($"å½ååºåç¶æä¸å
许è¿ç«æä½ï¼å½åç¶æï¼{stockInfo.StockStatus}"); |
| | | } |
| | | |
| | | // 4. è·åç³»ç»é
ç½® - ç´æ¥ä»æ°æ®åºæ¥è¯¢ |
| | | var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" }); |
| | | |
| | | string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001"); |
| | | string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001"); |
| | | |
| | | // 5. æé MESè¯·æ± |
| | | // 4. æé MESè¯·æ± |
| | | var mesRequest = new InboundInContainerRequest |
| | | { |
| | | EquipmentCode = equipmentCode, |
| | | ResourceCode = resourceCode, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = dto.PalletCode |
| | | }; |
| | | |
| | | string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest); |
| | | |
| | | // 6. è°ç¨MESæ¥å£ï¼åæ¥æ¹æ³ï¼ |
| | | // 5. è°ç¨MESæ¥å£ï¼åæ¥æ¹æ³ï¼ |
| | | var mesResult = _mesService.InboundInContainer(mesRequest); |
| | | stopwatch.Stop(); |
| | | |
| | | // 7. è®°å½æ¥å¿ |
| | | // 6. è®°å½æ¥å¿ |
| | | await _mesLogService.LogAsync(new MesApiLogDto |
| | | { |
| | | ApiType = "InboundInContainer", |
| | |
| | | Creator = App.User.UserName |
| | | }); |
| | | |
| | | // 8. è¿åç»æ |
| | | // 7. è¿åç»æ |
| | | if (mesResult.IsSuccess) |
| | | { |
| | | return response.OK("æçè¿ç«æå"); |
| | |
| | | return response.Error($"å½ååºåç¶æä¸å
许åºç«æä½ï¼å½åç¶æï¼{stockInfo.StockStatus}"); |
| | | } |
| | | |
| | | // 4. è·åç³»ç»é
ç½® |
| | | var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" }); |
| | | |
| | | string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001"); |
| | | string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001"); |
| | | |
| | | // 5. æé MESè¯·æ± |
| | | // 4. æé MESè¯·æ± |
| | | var mesRequest = new OutboundInContainerRequest |
| | | { |
| | | EquipmentCode = equipmentCode, |
| | | ResourceCode = resourceCode, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = dto.PalletCode, |
| | | ParamList = dto.ParamList?.Select(p => new ParamItem |
| | |
| | | |
| | | string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest); |
| | | |
| | | // 6. è°ç¨MESæ¥å£ï¼åæ¥æ¹æ³ï¼ |
| | | // 5. è°ç¨MESæ¥å£ï¼åæ¥æ¹æ³ï¼ |
| | | var mesResult = _mesService.OutboundInContainer(mesRequest); |
| | | stopwatch.Stop(); |
| | | |
| | | // 7. è®°å½æ¥å¿ |
| | | // 6. è®°å½æ¥å¿ |
| | | await _mesLogService.LogAsync(new MesApiLogDto |
| | | { |
| | | ApiType = "OutboundInContainer", |
| | |
| | | Creator = App.User.UserName |
| | | }); |
| | | |
| | | // 8. è¿åç»æ |
| | | // 7. è¿åç»æ |
| | | if (mesResult.IsSuccess) |
| | | { |
| | | return response.OK("æçåºç«æå"); |
| | |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IMesService; |
| | | using WIDESEA_ISystemService; |
| | | using WIDESEA_Model.Models; |
| | | using System.Diagnostics; |
| | |
| | | private readonly IMesLogService _mesLogService; |
| | | private readonly IMesService _mesService; |
| | | private readonly ISys_DictionaryService _sysDictionaryService; |
| | | private readonly IStockInfoService _stockInfoService; |
| | | |
| | | public StockInfoDetailController( |
| | | IStockInfoDetailService service, |
| | | IMesLogService mesLogService, |
| | | IMesService mesService, |
| | | ISys_DictionaryService sysDictionaryService) : base(service) |
| | | ISys_DictionaryService sysDictionaryService, |
| | | IStockInfoService stockInfoService) : base(service) |
| | | { |
| | | _mesLogService = mesLogService; |
| | | _mesService = mesService; |
| | | _sysDictionaryService = sysDictionaryService; |
| | | _stockInfoService = stockInfoService; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | try |
| | | { |
| | | // 1. åæ°éªè¯ |
| | | if (string.IsNullOrWhiteSpace(dto.PalletCode)) |
| | | { |
| | | return response.Error("æçç¼å·ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | if (dto.SfcList == null || !dto.SfcList.Any()) |
| | | { |
| | | return response.Error("çµè¯ç å表ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | // 2. éªè¯çµè¯ç¶æï¼é'å·²éå®'ç¶æå
许ç»å®ï¼ |
| | | var stockDetails = await Service.Repository.QueryDataAsync(x => dto.SfcList.Contains(x.SerialNumber)); |
| | | if (stockDetails != null && stockDetails.Any(d => d.Status == 99)) |
| | | var stockDetail = await Service.Repository.QueryFirstAsync(x => dto.SfcList.Contains(x.SerialNumber)); |
| | | if (stockDetail != null && stockDetail.Status == 99) |
| | | { |
| | | return response.Error("å½ååºåæç»å
å«å·²éå®ç¶æï¼ä¸å
许æ§è¡ç»å®æä½"); |
| | | } |
| | | |
| | | // 3. è·åç³»ç»é
ç½® |
| | | var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" }); |
| | | |
| | | string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001"); |
| | | string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001"); |
| | | var stockInfo = await _stockInfoService.Repository.QueryFirstAsync(x => stockDetail.StockId == x.Id); |
| | | |
| | | // 3. æé MESè¯·æ± - å°çµè¯å表转æ¢ä¸ºContainerSfcItemæ ¼å¼ |
| | | var mesRequest = new BindContainerRequest |
| | | { |
| | | EquipmentCode = equipmentCode, |
| | | ResourceCode = resourceCode, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = dto.PalletCode, |
| | | ContainerCode = stockInfo.PalletCode, |
| | | ContainerSfcList = dto.SfcList.Select(sfc => new ContainerSfcItem |
| | | { |
| | | Sfc = sfc, |
| | |
| | | try |
| | | { |
| | | // 1. åæ°éªè¯ |
| | | if (string.IsNullOrWhiteSpace(dto.PalletCode)) |
| | | { |
| | | return response.Error("æçç¼å·ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | if (dto.SfcList == null || !dto.SfcList.Any()) |
| | | { |
| | | return response.Error("çµè¯ç å表ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | // 2. éªè¯çµè¯ç¶æï¼é'å·²éå®'ç¶æå
许解ç»ï¼ |
| | | var stockDetails = await Service.Repository.QueryDataAsync(x => dto.SfcList.Contains(x.SerialNumber)); |
| | | if (stockDetails != null && stockDetails.Any(d => d.Status == 99)) |
| | | var stockDetail = await Service.Repository.QueryFirstAsync(x => dto.SfcList.Contains(x.SerialNumber)); |
| | | if (stockDetail != null && stockDetail.Status == 99) |
| | | { |
| | | return response.Error("å½ååºåæç»å
å«å·²éå®ç¶æï¼ä¸å
许æ§è¡è§£ç»æä½"); |
| | | } |
| | | |
| | | // 3. è·åç³»ç»é
ç½® |
| | | var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" }); |
| | | |
| | | string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001"); |
| | | string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001"); |
| | | var stockInfo = await _stockInfoService.Repository.QueryFirstAsync(x => stockDetail.StockId == x.Id); |
| | | |
| | | // 3. æé MESè¯·æ± |
| | | var mesRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = equipmentCode, |
| | | ResourceCode = resourceCode, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = dto.PalletCode, |
| | | ContainCode = stockInfo.PalletCode, |
| | | SfcList = dto.SfcList |
| | | }; |
| | | |
| | |
| | | |
| | | // 2. éªè¯çµè¯ç¶æï¼é'å·²éå®'ç¶æå
许NG䏿¥ï¼ |
| | | var sfcList = dto.NgSfcList.Select(x => x.Sfc).ToList(); |
| | | var stockDetails = await Service.Repository.QueryDataAsync(x => sfcList.Contains(x.SerialNumber)); |
| | | if (stockDetails != null && stockDetails.Any(d => d.Status == 99)) |
| | | var stockDetail = await Service.Repository.QueryFirstAsync(x => sfcList.Contains(x.SerialNumber)); |
| | | if (stockDetail != null && stockDetail.Status == 99) |
| | | { |
| | | return response.Error("å½ååºåæç»å
å«å·²éå®ç¶æï¼ä¸å
许æ§è¡NG䏿¥æä½"); |
| | | } |
| | | |
| | | // 3. è·åç³»ç»é
ç½® |
| | | var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" }); |
| | | |
| | | string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001"); |
| | | string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001"); |
| | | var stockInfo = await _stockInfoService.Repository.QueryFirstAsync(x => stockDetail.StockId == x.Id); |
| | | |
| | | // 3. æé MESè¯·æ± - å°DTOæ ¼å¼è½¬æ¢ä¸ºMESè¯·æ±æ ¼å¼ |
| | | var mesRequest = new ContainerNgReportRequest |
| | | { |
| | | EquipmentCode = equipmentCode, |
| | | ResourceCode = resourceCode, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "RESOURCE-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = dto.PalletCode, |
| | | ContainerCode = stockInfo.PalletCode, |
| | | NgSfcList = dto.NgSfcList.Select(ng => new NgSfcItem |
| | | { |
| | | Sfc = ng.Sfc, |
| | |
| | | <ProjectReference Include="..\WIDESEA_BasicService\WIDESEA_BasicService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_CheckService\WIDESEA_CheckService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_InboundService\WIDESEA_InboundService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_MesService\WIDESEA_MesService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_OutboundService\WIDESEA_OutboundService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_RecordService\WIDESEA_RecordService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_StockService\WIDESEA_StockService.csproj" /> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # MES æ¥å£è°ç¨æ¥å¿é¡µé¢å®æ½è®¡å |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. |
| | | |
| | | **ç®æ :** å¨ WMS ç³»ç»ä¸æ·»å MES æ¥å£è°ç¨æ¥å¿æ¥ç页é¢ï¼æä¾ç»¼åæ§çæ¥å¿æ¥è¯¢ãç»è®¡å管çåè½ã |
| | | |
| | | **æ¶æ:** åç«¯ä½¿ç¨ C# / ASP.NET Coreï¼åç«¯ä½¿ç¨ Vue 3 + Element Plusãå¤ç¨ç°æ view-grid 模å¼ï¼æ·»å èªå®ä¹ç»è®¡å¡çã |
| | | |
| | | **ææ¯æ :** .NET 8, SqlSugar, Vue 3, Element Plus 2.2.14, Vite |
| | | |
| | | **çæ¬:** v0.3 (修订ç - ä¿®å¤å®¡æ¥é®é¢) |
| | | |
| | | --- |
| | | |
| | | ## è®¾è®¡ææ¡£ |
| | | |
| | | åè: `docs/superpowers/specs/2026-04-13-mes-api-log-page-design.md` |
| | | |
| | | --- |
| | | |
| | | ## æä»¶ç»æ |
| | | |
| | | ### å端æ°å»ºæä»¶ |
| | | ``` |
| | | WMS/WIDESEA_WMSServer/ |
| | | âââ WIDESEA_DTO/MES/ |
| | | â âââ MesLogQueryDto.cs # æ¥è¯¢è¯·æ± DTO |
| | | â âââ MesLogStatisticsDto.cs # ç»è®¡æ°æ® DTO |
| | | â âââ MesLogListItemDto.cs # å表项 DTO |
| | | â âââ MesLogDetailDto.cs # 详æ
DTO |
| | | âââ WIDESEA_IMesService/ |
| | | â âââ IMesLogService.cs # æ©å±æå¡æ¥å£ |
| | | âââ WIDESEA_MesService/ |
| | | â âââ MesLogService.cs # æå¡å®ç° |
| | | âââ WIDESEA_WMSServer/Controllers/Mes/ |
| | | âââ MesLogController.cs # API æ§å¶å¨ |
| | | ``` |
| | | |
| | | ### å端æ°å»ºæä»¶ |
| | | ``` |
| | | WMS/WIDESEA_WMSClient/src/ |
| | | âââ components/ |
| | | â âââ MesLogStatistics.vue # ç»è®¡å¡çç»ä»¶ |
| | | âââ views/system/ |
| | | â âââ Mes_Log.vue # æ¥å¿é¡µé¢ |
| | | âââ extension/system/ |
| | | âââ Mes_Log.jsx # ä¸å¡æ©å±é»è¾ |
| | | ``` |
| | | |
| | | ### ä¿®æ¹æä»¶ |
| | | ``` |
| | | WMS/WIDESEA_WMSClient/src/ |
| | | âââ router/viewGird.js # æ·»å è·¯ç±é
ç½® |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 1: å建å端 DTO æä»¶ |
| | | |
| | | **Files:** |
| | | - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogQueryDto.cs` |
| | | - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogStatisticsDto.cs` |
| | | - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogListItemDto.cs` |
| | | - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogDetailDto.cs` |
| | | |
| | | ### Step 1: å建 MesLogQueryDto.cs |
| | | |
| | | ```csharp |
| | | using System; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿æ¥è¯¢è¯·æ±DTO |
| | | /// </summary> |
| | | public class MesLogQueryDto |
| | | { |
| | | /// <summary> |
| | | /// æ¥å£ç±»å: BindContainer, UnBindContainer, ContainerNgReport, InboundInContainer, OutboundInContainer |
| | | /// </summary> |
| | | public string ApiType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåç¶æ: null-å
¨é¨, true-æå, false-失败 |
| | | /// </summary> |
| | | public bool? IsSuccess { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¼å§æ¶é´ï¼å端 dateRange åæ®µæ å°ï¼dateRange[0] â StartTimeï¼ |
| | | /// </summary> |
| | | public DateTime? StartTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç»ææ¶é´ï¼å端 dateRange åæ®µæ å°ï¼dateRange[1] â EndTimeï¼ |
| | | /// </summary> |
| | | public DateTime? EndTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åå»ºäººï¼æä½äººï¼ |
| | | /// </summary> |
| | | public string Creator { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå°èæ¶ï¼æ¯«ç§ï¼ï¼å端 elapsedRange åæ®µæ å°ï¼elapsedRange[0] â MinElapsedMsï¼ |
| | | /// </summary> |
| | | public int? MinElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§èæ¶ï¼æ¯«ç§ï¼ï¼å端 elapsedRange åæ®µæ å°ï¼elapsedRange[1] â MaxElapsedMsï¼ |
| | | /// </summary> |
| | | public int? MaxElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é误信æ¯å
³é®å |
| | | /// </summary> |
| | | public string ErrorKeyword { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±JSONå
³é®å |
| | | /// </summary> |
| | | public string JsonRequestKeyword { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºJSONå
³é®å |
| | | /// </summary> |
| | | public string JsonResponseKeyword { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### Step 2: å建 MesLogStatisticsDto.cs |
| | | |
| | | ```csharp |
| | | using System; |
| | | using System.Collections.Generic; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿ç»è®¡æ°æ®DTO |
| | | /// </summary> |
| | | public class MesLogStatisticsDto |
| | | { |
| | | /// <summary> |
| | | /// æ»è°ç¨æ¬¡æ° |
| | | /// </summary> |
| | | public int TotalCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåæ¬¡æ° |
| | | /// </summary> |
| | | public int SuccessCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåçï¼ç¾åæ¯ï¼ |
| | | /// </summary> |
| | | public double SuccessRate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¤±è´¥æ¬¡æ° |
| | | /// </summary> |
| | | public int FailedCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¹³åèæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public double AvgElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§èæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public int MaxElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 仿¥è°ç¨æ¬¡æ° |
| | | /// </summary> |
| | | public int TodayCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 忥å£ç±»åè°ç¨æ¬¡æ°ç»è®¡ |
| | | /// </summary> |
| | | public Dictionary<string, int> ApiTypeCounts { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### Step 3: å建 MesLogListItemDto.cs |
| | | |
| | | ```csharp |
| | | using System; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿å表项DTO |
| | | /// </summary> |
| | | public class MesLogListItemDto |
| | | { |
| | | /// <summary> |
| | | /// 主é®ID |
| | | /// </summary> |
| | | public long Id { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¥å£ç±»å |
| | | /// </summary> |
| | | public string ApiType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¯å¦æå |
| | | /// </summary> |
| | | public bool IsSuccess { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±JSONé¢è§ï¼å200åç¬¦ï¼ |
| | | /// </summary> |
| | | public string RequestJsonPreview { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºJSONé¢è§ï¼å200åç¬¦ï¼ |
| | | /// </summary> |
| | | public string ResponseJsonPreview { get; set; } |
| | | |
| | | /// <summary> |
| | | /// éè¯¯ä¿¡æ¯ |
| | | /// </summary> |
| | | public string ErrorMessage { get; set; } |
| | | |
| | | /// <summary> |
| | | /// èæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public int ElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å建æ¶é´ |
| | | /// </summary> |
| | | public DateTime CreateDate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å建人 |
| | | /// </summary> |
| | | public string Creator { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### Step 4: å建 MesLogDetailDto.cs |
| | | |
| | | ```csharp |
| | | using System; |
| | | |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿è¯¦æ
DTO |
| | | /// </summary> |
| | | public class MesLogDetailDto : MesLogListItemDto |
| | | { |
| | | /// <summary> |
| | | /// 宿´è¯·æ±JSON |
| | | /// </summary> |
| | | public string RequestJson { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 宿´ååºJSON |
| | | /// </summary> |
| | | public string ResponseJson { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¿®æ¹æ¶é´ |
| | | /// </summary> |
| | | public DateTime? ModifyDate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¿®æ¹äºº |
| | | /// </summary> |
| | | public string Modifier { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### Step 5: æäº¤ DTO æä»¶ |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLog*.cs |
| | | git commit -m "feat(MES): æ·»å MESæ¥å¿æ¥è¯¢DTOæä»¶ |
| | | |
| | | - MesLogQueryDto: æ¥è¯¢è¯·æ±åæ° |
| | | - MesLogStatisticsDto: ç»è®¡æ°æ® |
| | | - MesLogListItemDto: å表项 |
| | | - MesLogDetailDto: 详æ
|
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 2: æ©å± IMesLogService æ¥å£ |
| | | |
| | | **Files:** |
| | | - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs` |
| | | |
| | | ### Step 1: 读åç°ææ¥å£ |
| | | |
| | | ```bash |
| | | cat WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs |
| | | ``` |
| | | |
| | | ### Step 2: æ·»å æ°æ¹æ³å°æ¥å£ |
| | | |
| | | å¨ç°ææ¥å£ä¸æ·»å 以䏿¹æ³ï¼ |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <param name="page">页ç </param> |
| | | /// <param name="pageSize">æ¯é¡µæ°é</param> |
| | | /// <returns>æ¥å¿åè¡¨åæ»æ°</returns> |
| | | Task<(List<MesLogListItemDto> items, int total)> GetPageAsync(MesLogQueryDto query, int page, int pageSize); |
| | | |
| | | /// <summary> |
| | | /// è·ååæ¡æ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | /// <param name="id">æ¥å¿ID</param> |
| | | /// <returns>æ¥å¿è¯¦æ
</returns> |
| | | Task<MesLogDetailDto> GetDetailAsync(long id); |
| | | |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <returns>ç»è®¡æ°æ®</returns> |
| | | Task<MesLogStatisticsDto> GetStatisticsAsync(MesLogQueryDto query); |
| | | |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿æ°æ® |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <returns>Excelæä»¶åèæ°ç»</returns> |
| | | Task<byte[]> ExportAsync(MesLogQueryDto query); |
| | | ``` |
| | | |
| | | ### Step 3: æ·»å using è¯å¥ |
| | | |
| | | ç¡®ä¿æä»¶é¡¶é¨æï¼ |
| | | ```csharp |
| | | using WIDESEA_DTO.MES; |
| | | ``` |
| | | |
| | | ### Step 4: æäº¤æ¥å£æ©å± |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs |
| | | git commit -m "feat(MES): æ©å±IMesLogServiceæ¥å£ |
| | | |
| | | - æ·»å å页æ¥è¯¢æ¹æ³ GetPageAsync |
| | | - æ·»å 详æ
æ¥è¯¢æ¹æ³ GetDetailAsync |
| | | - æ·»å ç»è®¡æ¥è¯¢æ¹æ³ GetStatisticsAsync |
| | | - æ·»å å¯¼åºæ¹æ³ ExportAsync |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 3: å®ç° MesLogService æ¹æ³ |
| | | |
| | | **Files:** |
| | | - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs` |
| | | |
| | | ### Step 1: 读åç°ææå¡å®ç° |
| | | |
| | | ```bash |
| | | cat WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs |
| | | ``` |
| | | |
| | | ### Step 2: æ·»å GetPageAsync å®ç° |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | public async Task<(List<MesLogListItemDto> items, int total)> GetPageAsync(MesLogQueryDto query, int page, int pageSize) |
| | | { |
| | | var dbQuery = _db.Queryable<WIDESEA_Model.Models.Mes.Dt_MesApiLog>(); |
| | | |
| | | // 卿æ¡ä»¶çé |
| | | if (!string.IsNullOrEmpty(query.ApiType)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ApiType == query.ApiType); |
| | | } |
| | | |
| | | if (query.IsSuccess.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.IsSuccess == query.IsSuccess.Value); |
| | | } |
| | | |
| | | if (query.StartTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate >= query.StartTime.Value); |
| | | } |
| | | |
| | | if (query.EndTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate <= query.EndTime.Value); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.Creator)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.Creator.Contains(query.Creator)); |
| | | } |
| | | |
| | | if (query.MinElapsedMs.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ElapsedMs >= query.MinElapsedMs.Value); |
| | | } |
| | | |
| | | if (query.MaxElapsedMs.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ElapsedMs <= query.MaxElapsedMs.Value); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.ErrorKeyword)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ErrorMessage.Contains(query.ErrorKeyword)); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.JsonRequestKeyword)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.RequestJson.Contains(query.JsonRequestKeyword)); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(query.JsonResponseKeyword)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ResponseJson.Contains(query.JsonResponseKeyword)); |
| | | } |
| | | |
| | | // è·åæ»æ° |
| | | var total = await dbQuery.CountAsync(); |
| | | |
| | | // å页æ¥è¯¢ |
| | | var entities = await dbQuery |
| | | .OrderByDescending(x => x.CreateDate) |
| | | .Skip((page - 1) * pageSize) |
| | | .Take(pageSize) |
| | | .ToListAsync(); |
| | | |
| | | // æ å°å° DTO |
| | | var items = entities.Select(e => new MesLogListItemDto |
| | | { |
| | | Id = e.Id, |
| | | ApiType = e.ApiType, |
| | | IsSuccess = e.IsSuccess, |
| | | RequestJsonPreview = e.RequestJson?.Length > 200 ? e.RequestJson.Substring(0, 200) + "..." : e.RequestJson, |
| | | ResponseJsonPreview = e.ResponseJson?.Length > 200 ? e.ResponseJson.Substring(0, 200) + "..." : e.ResponseJson, |
| | | ErrorMessage = e.ErrorMessage, |
| | | ElapsedMs = e.ElapsedMs, |
| | | CreateDate = e.CreateDate, |
| | | Creator = e.Creator |
| | | }).ToList(); |
| | | |
| | | return (items, total); |
| | | } |
| | | ``` |
| | | |
| | | ### Step 3: æ·»å GetDetailAsync å®ç° |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// è·ååæ¡æ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | public async Task<MesLogDetailDto> GetDetailAsync(long id) |
| | | { |
| | | var entity = await _db.Queryable<WIDESEA_Model.Models.Mes.Dt_MesApiLog>() |
| | | .FirstAsync(x => x.Id == id); |
| | | |
| | | if (entity == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | return new MesLogDetailDto |
| | | { |
| | | Id = entity.Id, |
| | | ApiType = entity.ApiType, |
| | | IsSuccess = entity.IsSuccess, |
| | | RequestJson = entity.RequestJson, |
| | | ResponseJson = entity.ResponseJson, |
| | | RequestJsonPreview = entity.RequestJson?.Length > 200 ? entity.RequestJson.Substring(0, 200) + "..." : entity.RequestJson, |
| | | ResponseJsonPreview = entity.ResponseJson?.Length > 200 ? entity.ResponseJson.Substring(0, 200) + "..." : entity.ResponseJson, |
| | | ErrorMessage = entity.ErrorMessage, |
| | | ElapsedMs = entity.ElapsedMs, |
| | | CreateDate = entity.CreateDate, |
| | | Creator = entity.Creator, |
| | | ModifyDate = entity.ModifyDate, |
| | | Modifier = entity.Modifier |
| | | }; |
| | | } |
| | | ``` |
| | | |
| | | ### Step 4: æ·»å GetStatisticsAsync å®ç° |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | public async Task<MesLogStatisticsDto> GetStatisticsAsync(MesLogQueryDto query) |
| | | { |
| | | var dbQuery = _db.Queryable<WIDESEA_Model.Models.Mes.Dt_MesApiLog>(); |
| | | |
| | | // åºç¨ç¸åçç鿡件ï¼ä½ä¸åé¡µï¼ |
| | | if (!string.IsNullOrEmpty(query.ApiType)) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.ApiType == query.ApiType); |
| | | } |
| | | |
| | | if (query.IsSuccess.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.IsSuccess == query.IsSuccess.Value); |
| | | } |
| | | |
| | | if (query.StartTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate >= query.StartTime.Value); |
| | | } |
| | | |
| | | if (query.EndTime.HasValue) |
| | | { |
| | | dbQuery = dbQuery.Where(x => x.CreateDate <= query.EndTime.Value); |
| | | } |
| | | |
| | | var allData = await dbQuery.ToListAsync(); |
| | | |
| | | // 仿¥æ°æ® |
| | | var today = DateTime.Today; |
| | | var todayData = allData.Where(x => x.CreateDate >= today).ToList(); |
| | | |
| | | // 计ç®ç»è®¡æ°æ® |
| | | var totalCount = allData.Count; |
| | | var successCount = allData.Count(x => x.IsSuccess); |
| | | var failedCount = totalCount - successCount; |
| | | var successRate = totalCount > 0 ? (successCount * 100.0 / totalCount) : 0; |
| | | |
| | | return new MesLogStatisticsDto |
| | | { |
| | | TotalCount = totalCount, |
| | | SuccessCount = successCount, |
| | | FailedCount = failedCount, |
| | | SuccessRate = Math.Round(successRate, 2), |
| | | AvgElapsedMs = totalCount > 0 ? allData.Average(x => x.ElapsedMs) : 0, |
| | | MaxElapsedMs = totalCount > 0 ? allData.Max(x => x.ElapsedMs) : 0, |
| | | TodayCount = todayData.Count, |
| | | ApiTypeCounts = allData.GroupBy(x => x.ApiType) |
| | | .ToDictionary(g => g.Key, g => g.Count()) |
| | | }; |
| | | } |
| | | ``` |
| | | |
| | | ### Step 5: æ·»å ExportAsync å®ç° |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿æ°æ® |
| | | /// </summary> |
| | | public async Task<byte[]> ExportAsync(MesLogQueryDto query) |
| | | { |
| | | // è·åææç¬¦åæ¡ä»¶çæ°æ®ï¼ä¸åé¡µï¼ |
| | | var (items, _) = await GetPageAsync(query, 1, int.MaxValue); |
| | | |
| | | // ä½¿ç¨æ¡æ¶å
ç½®ç Excel 导åºåè½ |
| | | // 注æï¼è¿ééè¦æ ¹æ®é¡¹ç®å®é
使ç¨ç导åºåºè°æ´ |
| | | // åè ServiceBase.Export() çå®ç°æ¹å¼ |
| | | |
| | | // ä¸´æ¶æ¹æ¡ï¼ä½¿ç¨ CSV æ ¼å¼ |
| | | using var memoryStream = new MemoryStream(); |
| | | using var writer = new StreamWriter(memoryStream, System.Text.Encoding.UTF8); |
| | | |
| | | // CSV å¤´é¨ |
| | | writer.WriteLine("ID,æ¥å£ç±»å,ç¶æ,èæ¶(ms),é误信æ¯,å建æ¶é´,å建人"); |
| | | |
| | | // CSV æ°æ®è¡ |
| | | foreach (var item in items) |
| | | { |
| | | writer.WriteLine($"{item.Id},{item.ApiType},{(item.IsSuccess ? "æå" : "失败")},{item.ElapsedMs},\"{item.ErrorMessage?.Replace("\"", "\"\"")}\",{item.CreateDate:yyyy-MM-dd HH:mm:ss},{item.Creator}"); |
| | | } |
| | | |
| | | writer.Flush(); |
| | | return memoryStream.ToArray(); |
| | | } |
| | | ``` |
| | | |
| | | ### Step 6: æ·»å using è¯å¥ |
| | | |
| | | ç¡®ä¿æä»¶é¡¶é¨æï¼ |
| | | ```csharp |
| | | using WIDESEA_DTO.MES; |
| | | ``` |
| | | |
| | | ### Step 7: æäº¤æå¡å®ç° |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs |
| | | git commit -m "feat(MES): å®ç°MesLogServiceæ¥è¯¢æ¹æ³ |
| | | |
| | | - å®ç°å页æ¥è¯¢ GetPageAsync |
| | | - å®ç°è¯¦æ
æ¥è¯¢ GetDetailAsync |
| | | - å®ç°ç»è®¡æ¥è¯¢ GetStatisticsAsync |
| | | - å®ç°å¯¼åº ExportAsync (CSVæ ¼å¼) |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 4: å建 MesLogController |
| | | |
| | | **Files:** |
| | | - Create: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Mes/MesLogController.cs` |
| | | |
| | | ### Step 1: å建æ§å¶å¨æä»¶ |
| | | |
| | | ```csharp |
| | | using Microsoft.AspNetCore.Authorization; |
| | | using Microsoft.AspNetCore.Http; |
| | | using Microsoft.AspNetCore.Mvc; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IMesService; |
| | | |
| | | namespace WIDESEA_WMSServer.Controllers.Mes |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å£æ¥å¿æ§å¶å¨ |
| | | /// </summary> |
| | | [Route("api/MesLog")] |
| | | [ApiController] |
| | | [Authorize] |
| | | public class MesLogController : ControllerBase |
| | | { |
| | | private readonly IMesLogService _mesLogService; |
| | | |
| | | /// <summary> |
| | | /// æé 彿° |
| | | /// </summary> |
| | | public MesLogController(IMesLogService mesLogService) |
| | | { |
| | | _mesLogService = mesLogService; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <param name="page">页ç ï¼é»è®¤1</param> |
| | | /// <param name="pageSize">æ¯é¡µæ°éï¼é»è®¤20</param> |
| | | /// <returns>åé¡µç»æ</returns> |
| | | [HttpPost("page")] |
| | | public async Task<WebResponseContent> GetPage([FromBody] MesLogQueryDto query, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) |
| | | { |
| | | var response = new WebResponseContent(); |
| | | try |
| | | { |
| | | var (items, total) = await _mesLogService.GetPageAsync(query, page, pageSize); |
| | | return response.OK(null, new { rows = items, total = total }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return response.Error($"æ¥è¯¢å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | /// <param name="id">æ¥å¿ID</param> |
| | | /// <returns>æ¥å¿è¯¦æ
</returns> |
| | | [HttpGet("{id}")] |
| | | public async Task<WebResponseContent> GetDetail(long id) |
| | | { |
| | | var response = new WebResponseContent(); |
| | | try |
| | | { |
| | | var detail = await _mesLogService.GetDetailAsync(id); |
| | | if (detail == null) |
| | | { |
| | | return response.Error("æ¥å¿ä¸åå¨"); |
| | | } |
| | | return response.OK(null, detail); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return response.Error($"æ¥è¯¢å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶ï¼å¯éï¼</param> |
| | | /// <returns>ç»è®¡æ°æ®</returns> |
| | | [HttpGet("statistics")] |
| | | public async Task<WebResponseContent> GetStatistics([FromQuery] MesLogQueryDto query) |
| | | { |
| | | var response = new WebResponseContent(); |
| | | try |
| | | { |
| | | var statistics = await _mesLogService.GetStatisticsAsync(query ?? new MesLogQueryDto()); |
| | | return response.OK(null, statistics); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return response.Error($"æ¥è¯¢å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿ |
| | | /// </summary> |
| | | /// <param name="query">æ¥è¯¢æ¡ä»¶</param> |
| | | /// <returns>Excelæä»¶</returns> |
| | | [HttpPost("export")] |
| | | public async Task<IActionResult> Export([FromBody] MesLogQueryDto query) |
| | | { |
| | | try |
| | | { |
| | | var data = await _mesLogService.ExportAsync(query ?? new MesLogQueryDto()); |
| | | var fileName = $"MESæ¥å£æ¥å¿_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; |
| | | return File(data, "text/csv; charset=utf-8", fileName); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return BadRequest(new { error = ex.Message }); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### Step 2: æäº¤æ§å¶å¨ |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Mes/MesLogController.cs |
| | | git commit -m "feat(MES): æ·»å MesLogControlleræ§å¶å¨ |
| | | |
| | | - POST /api/MesLog/page - å页æ¥è¯¢ |
| | | - GET /api/MesLog/{id} - 详æ
æ¥è¯¢ |
| | | - GET /api/MesLog/statistics - ç»è®¡æ°æ® |
| | | - POST /api/MesLog/export - 导åºCSV |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 5: å建å端ç»è®¡å¡çç»ä»¶ |
| | | |
| | | **Files:** |
| | | - Create: `WMS/WIDESEA_WMSClient/src/components/MesLogStatistics.vue` |
| | | |
| | | ### Step 1: å建ç»ä»¶æä»¶ |
| | | |
| | | ```vue |
| | | <template> |
| | | <div class="mes-log-statistics"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-primary"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">æ»è°ç¨æ¬¡æ°</div> |
| | | <div class="stat-value">{{ statistics.totalCount }}</div> |
| | | <div class="stat-unit">次</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card" :class="statistics.successRate >= 90 ? 'stat-success' : 'stat-warning'"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">æåç</div> |
| | | <div class="stat-value">{{ statistics.successRate }}%</div> |
| | | <div class="stat-sub"> |
| | | æå: {{ statistics.successCount }} / 失败: {{ statistics.failedCount }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-info"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">å¹³åèæ¶</div> |
| | | <div class="stat-value">{{ Math.round(statistics.avgElapsedMs) }}</div> |
| | | <div class="stat-unit">ms</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-secondary"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">仿¥è°ç¨</div> |
| | | <div class="stat-value">{{ statistics.todayCount }}</div> |
| | | <div class="stat-unit">次</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ref, onMounted } from "vue"; |
| | | import http from "@/api/axios"; |
| | | |
| | | export default { |
| | | name: "MesLogStatistics", |
| | | emits: ["refresh"], |
| | | setup(props, { emit }) { |
| | | const statistics = ref({ |
| | | totalCount: 0, |
| | | successCount: 0, |
| | | failedCount: 0, |
| | | successRate: 0, |
| | | avgElapsedMs: 0, |
| | | todayCount: 0 |
| | | }); |
| | | |
| | | const fetchStatistics = async () => { |
| | | try { |
| | | const res = await http.get("/api/MesLog/statistics"); |
| | | if (res.status) { |
| | | statistics.value = res.data; |
| | | emit("refresh"); |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åç»è®¡æ°æ®å¤±è´¥:", error); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchStatistics(); |
| | | }); |
| | | |
| | | return { |
| | | statistics, |
| | | fetchStatistics |
| | | }; |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .mes-log-statistics { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .stat-card { |
| | | text-align: center; |
| | | } |
| | | |
| | | .stat-content { |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 28px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .stat-sub { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .stat-primary .stat-value { color: #409EFF; } |
| | | .stat-success .stat-value { color: #67C23A; } |
| | | .stat-warning .stat-value { color: #E6A23C; } |
| | | .stat-info .stat-value { color: #909399; } |
| | | .stat-secondary .stat-value { color: #909399; } |
| | | </style> |
| | | ``` |
| | | |
| | | ### Step 2: æäº¤ç»ä»¶ |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSClient/src/components/MesLogStatistics.vue |
| | | git commit -m "feat(MES): æ·»å MesLogStatisticsç»è®¡å¡çç»ä»¶ |
| | | |
| | | - æ¾ç¤ºæ»è°ç¨æ¬¡æ°ãæåçãå¹³åèæ¶ã仿¥è°ç¨ |
| | | - ä½¿ç¨ el-card èªå®ä¹å®ç°ï¼å
¼å®¹ Element Plus 2.2.14ï¼ |
| | | - é¢è²æ è¯ï¼èè²/绿è²/æ©è²/ç°è² |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 7: å建å端æ©å±é»è¾ |
| | | |
| | | **Files:** |
| | | - Create: `WMS/WIDESEA_WMSClient/src/extension/system/Mes_Log.jsx` |
| | | |
| | | ### Step 1: å建æ©å±æä»¶ |
| | | |
| | | ```jsx |
| | | import { h, resolveComponent } from 'vue'; |
| | | |
| | | let extension = { |
| | | components: { |
| | | // 卿æ©å
ç»ä»¶æç»ä»¶è·¯å¾ |
| | | gridHeader: "", |
| | | gridBody: '', |
| | | gridFooter: "", |
| | | modelHeader: "", |
| | | modelBody: "", |
| | | modelFooter: "" |
| | | }, |
| | | buttons: [], // æ©å±çæé® |
| | | methods: { |
| | | // äºä»¶æ©å± |
| | | onInit() { |
| | | console.log("mes_log init"); |
| | | this.setFiexdSearchForm(true); |
| | | }, |
| | | |
| | | onInited() { |
| | | this.height = this.height - 240; // 为ç»è®¡å¡çé¢çç©ºé´ |
| | | |
| | | // æ·»å é¢è§æ¹æ³ |
| | | this.previewJson = (jsonStr) => { |
| | | if (!jsonStr) return '-'; |
| | | try { |
| | | const obj = JSON.parse(jsonStr); |
| | | return JSON.stringify(obj, null, 2).substring(0, 200) + '...'; |
| | | } catch { |
| | | return String(jsonStr).substring(0, 200) + '...'; |
| | | } |
| | | }; |
| | | }, |
| | | |
| | | // è¡ç¹å»äºä»¶ - æ¾ç¤º JSON 详æ
|
| | | rowClick({ row, column }) { |
| | | // 妿ç¹å»çæ¯è¯·æ±æååºåï¼æ¾ç¤ºè¯¦æ
å¼¹çª |
| | | if (column.property === 'requestJson' || column.property === 'responseJson') { |
| | | this.showJsonDetail(row); |
| | | } |
| | | }, |
| | | |
| | | // æ¾ç¤º JSON 详æ
å¼¹çª |
| | | showJsonDetail(row) { |
| | | const elDialog = resolveComponent('el-dialog'); |
| | | |
| | | // å建临æ¶å¼¹çª |
| | | this.$alert('', 'JSON 详æ
', { |
| | | message: h('div', { class: 'json-detail-dialog' }, [ |
| | | h('el-tabs', { modelValue: 'request' }, [ |
| | | h('el-tab-pane', { label: '请æ±', name: 'request' }, [ |
| | | h('pre', { class: 'json-content' }, this.formatJson(row.requestJson)) |
| | | ]), |
| | | h('el-tab-pane', { label: 'ååº', name: 'response' }, [ |
| | | h('pre', { class: 'json-content' }, this.formatJson(row.responseJson)) |
| | | ]) |
| | | ]) |
| | | ]), |
| | | customClass: 'mes-json-detail-dialog', |
| | | dangerouslyUseHTMLString: false |
| | | }); |
| | | }, |
| | | |
| | | // æ ¼å¼å JSON |
| | | formatJson(jsonStr) { |
| | | if (!jsonStr) return '{}'; |
| | | try { |
| | | const obj = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr; |
| | | return JSON.stringify(obj, null, 2); |
| | | } catch { |
| | | return String(jsonStr); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | export default extension; |
| | | ``` |
| | | |
| | | ### Step 2: æäº¤æ©å±æä»¶ |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSClient/src/extension/system/Mes_Log.jsx |
| | | git commit -m "feat(MES): æ·»å Mes_Logæ©å±é»è¾ |
| | | |
| | | - æ·»å previewJson è¾
å©å½æ° |
| | | - æ·»å è¡ç¹å»äºä»¶å¤ç |
| | | - æ·»å JSON 详æ
æ¾ç¤ºåè½ |
| | | - è°æ´é«åº¦ä¸ºç»è®¡å¡çé¢çç©ºé´ |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 8: å建åç«¯é¡µé¢ |
| | | |
| | | **Files:** |
| | | - Create: `WMS/WIDESEA_WMSClient/src/views/system/Mes_Log.vue` |
| | | |
| | | ### Step 1: åå»ºé¡µé¢æä»¶ |
| | | |
| | | ```vue |
| | | <!-- |
| | | *Authorï¼System |
| | | *Contactï¼- |
| | | *代ç ç±æ¡æ¶çæ,任使´æ¹é½å¯è½å¯¼è´è¢«ä»£ç çæå¨è¦ç |
| | | *ä¸å¡è¯·å¨@/extension/system/Mes_Log.jsxæ¤å¤ç¼å |
| | | --> |
| | | <template> |
| | | <div class="mes-log-page"> |
| | | <!-- ç»è®¡å¡çåºå --> |
| | | <mes-log-statistics ref="statistics" /> |
| | | |
| | | <!-- æ¥å¿å表 --> |
| | | <view-grid |
| | | ref="grid" |
| | | :columns="columns" |
| | | :detail="detail" |
| | | :editFormFields="editFormFields" |
| | | :editFormOptions="editFormOptions" |
| | | :searchFormFields="searchFormFields" |
| | | :searchFormOptions="searchFormOptions" |
| | | :table="table" |
| | | :extend="extend" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import extend from "@/extension/system/Mes_Log.jsx"; |
| | | import MesLogStatistics from "@/components/MesLogStatistics.vue"; |
| | | import { ref, defineComponent } from "vue"; |
| | | |
| | | export default defineComponent({ |
| | | name: "Mes_Log", |
| | | components: { |
| | | MesLogStatistics |
| | | }, |
| | | setup() { |
| | | const table = ref({ |
| | | key: "Id", |
| | | cnName: "MESæ¥å£æ¥å¿", |
| | | name: "Mes_Log", |
| | | url: "/Mes_Log/", |
| | | sortName: "Id" |
| | | }); |
| | | |
| | | const columns = ref([ |
| | | { field: "id", title: "ID", width: 80, hidden: true }, |
| | | { |
| | | field: "apiType", |
| | | title: "æ¥å£ç±»å", |
| | | width: 130, |
| | | bind: { key: "mesApiType", data: [] } |
| | | }, |
| | | { |
| | | field: "isSuccess", |
| | | title: "ç¶æ", |
| | | width: 80, |
| | | bind: { key: "mesApiStatus", data: [] } |
| | | }, |
| | | { |
| | | field: "requestJson", |
| | | title: "请æ±å
容", |
| | | width: 200, |
| | | link: true |
| | | }, |
| | | { |
| | | field: "responseJson", |
| | | title: "ååºå
容", |
| | | width: 200, |
| | | link: true |
| | | }, |
| | | { field: "errorMessage", title: "é误信æ¯", width: 250 }, |
| | | { field: "elapsedMs", title: "èæ¶(ms)", width: 100, sortable: true }, |
| | | { field: "createDate", title: "è°ç¨æ¶é´", width: 160, sortable: true }, |
| | | { field: "creator", title: "æä½äºº", width: 100 } |
| | | ]); |
| | | |
| | | const detail = ref({ |
| | | cnName: "MESæ¥å¿è¯¦æ
", |
| | | columns: [], |
| | | sortName: "Id", |
| | | key: "Id" |
| | | }); |
| | | |
| | | const editFormFields = ref({}); |
| | | const editFormOptions = ref([]); |
| | | |
| | | const searchFormFields = ref({ |
| | | apiType: "", |
| | | isSuccess: "", |
| | | dateRange: "", |
| | | creator: "", |
| | | elapsedRange: "", |
| | | errorKeyword: "", |
| | | jsonKeyword: "" |
| | | }); |
| | | |
| | | const searchFormOptions = ref([ |
| | | [ |
| | | { field: "apiType", title: "æ¥å£ç±»å", type: "select" }, |
| | | { field: "isSuccess", title: "ç¶æ", type: "select" }, |
| | | { field: "dateRange", title: "æ¶é´èå´", type: "datetimeRange" } |
| | | ], |
| | | [ |
| | | { field: "creator", title: "æä½äºº", type: "text" }, |
| | | { |
| | | field: "elapsedRange", |
| | | title: "èæ¶èå´(ms)", |
| | | type: "numberRange", |
| | | placeholder: ["æå°", "æå¤§"] |
| | | } |
| | | ], |
| | | [ |
| | | { field: "errorKeyword", title: "é误å
³é®å", type: "text" }, |
| | | { field: "jsonKeyword", title: "JSONå
容å
³é®å", type: "text" } |
| | | ] |
| | | ]); |
| | | |
| | | return { |
| | | table, |
| | | columns, |
| | | detail, |
| | | editFormFields, |
| | | editFormOptions, |
| | | searchFormFields, |
| | | searchFormOptions, |
| | | extend |
| | | }; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .mes-log-page { |
| | | padding: 16px; |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | ### Step 2: æäº¤é¡µé¢æä»¶ |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSClient/src/views/system/Mes_Log.vue |
| | | git commit -m "feat(MES): æ·»å Mes_Logæ¥å¿é¡µé¢ |
| | | |
| | | - ä½¿ç¨ view-grid ç»ä»¶ |
| | | - éæç»è®¡å¡çç»ä»¶ |
| | | - é
ç½®æç´¢è¡¨ååå表å |
| | | - æ¯æå¤ç»´åº¦çé |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 9: æ·»å è·¯ç±é
ç½® |
| | | |
| | | **Files:** |
| | | - Modify: `WMS/WIDESEA_WMSClient/src/router/viewGird.js` |
| | | |
| | | ### Step 1: 读åè·¯ç±æä»¶ |
| | | |
| | | ```bash |
| | | cat WMS/WIDESEA_WMSClient/src/router/viewGird.js | head -50 |
| | | ``` |
| | | |
| | | ### Step 2: å¨ Sys_Log è·¯ç±åæ·»å Mes_Log è·¯ç± |
| | | |
| | | å¨ `viewGird.js` çè·¯ç±æ°ç»ä¸ï¼æ¾å° `path: '/Sys_Log'` é
ç½®åï¼å¤§çº¦å¨æä»¶å¼å¤´ï¼ï¼å¨å
¶åæ·»å ï¼ |
| | | |
| | | ```javascript |
| | | { |
| | | path: '/Sys_Log', |
| | | name: 'sys_Log', |
| | | component: () => import('@/views/system/Sys_Log.vue') |
| | | }, |
| | | { |
| | | path: '/Mes_Log', // â æ·»å æ¤å |
| | | name: 'mes_Log', |
| | | component: () => import('@/views/system/Mes_Log.vue') |
| | | }, |
| | | { |
| | | path: '/Sys_User', // ä¸ä¸ä¸ªè·¯ç± |
| | | name: 'Sys_User', |
| | | component: () => import('@/views/system/Sys_User.vue') |
| | | }, |
| | | ``` |
| | | |
| | | æå
¥ä½ç½®ï¼å¨ `Sys_Log` è·¯ç±åä¹åï¼`Sys_User` è·¯ç±åä¹å |
| | | |
| | | ### Step 3: æäº¤è·¯ç±é
ç½® |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSClient/src/router/viewGird.js |
| | | git commit -m "feat(MES): æ·»å Mes_Logè·¯ç±é
ç½® |
| | | |
| | | - æ·»å /Mes_Log è·¯ç± |
| | | - æå views/system/Mes_Log.vue |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 10: æ°æ®åºé
ç½® |
| | | |
| | | ### Step 1: æ§è¡ç´¢å¼åå»ºèæ¬ |
| | | |
| | | ```sql |
| | | -- å¨ SQL Server Management Studio 䏿§è¡ |
| | | |
| | | -- æ¥å£ç±»åç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_ApiType' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_ApiType ON Dt_MesApiLog(ApiType); |
| | | END |
| | | |
| | | -- å建æ¶é´ç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_CreateDate' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_CreateDate ON Dt_MesApiLog(CreateDate DESC); |
| | | END |
| | | |
| | | -- æåç¶æç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_IsSuccess' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_IsSuccess ON Dt_MesApiLog(IsSuccess); |
| | | END |
| | | |
| | | -- åå»ºäººç´¢å¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_Creator' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_Creator ON Dt_MesApiLog(Creator); |
| | | END |
| | | ``` |
| | | |
| | | ### Step 2: æ·»å èåè®°å½ |
| | | |
| | | ```sql |
| | | -- å
æ¥è¯¢ç³»ç»ç®¡çèåç ID |
| | | SELECT Id, MenuName FROM Dt_Menu WHERE MenuName LIKE '%ç³»ç»%' OR MenuName LIKE '%System%'; |
| | | |
| | | -- å设系ç»ç®¡çèå ID 为 XXXï¼æå
¥ MES æ¥å£æ¥å¿èå |
| | | INSERT INTO Dt_Menu (ParentId, MenuName, Url, Component, Permission, Sort, Icon, CreateDate, Modifier) |
| | | VALUES ( |
| | | XXX, -- æ¿æ¢ä¸ºå®é
çç³»ç»ç®¡çèå ID |
| | | 'MESæ¥å£æ¥å¿', |
| | | '/Mes_Log', |
| | | 'views/system/Mes_Log', |
| | | 'Mes_Log:view', |
| | | (SELECT ISNULL(MAX(Sort), 0) + 1 FROM Dt_Menu WHERE ParentId = XXX), |
| | | 'el-icon-document', |
| | | GETDATE(), |
| | | 'System' |
| | | ); |
| | | ``` |
| | | |
| | | ### Step 3: æ·»å æ°æ®åå
¸è®°å½ |
| | | |
| | | ```sql |
| | | -- æ¥å£ç±»ååå
¸ |
| | | DECLARE @DictId INT; |
| | | SELECT @DictId = Id FROM Dt_Dictionary WHERE DictKey = 'mesApiType'; |
| | | |
| | | IF @DictId IS NULL |
| | | BEGIN |
| | | INSERT INTO Dt_Dictionary (DictKey, DictValue, CreateDate, Modifier) |
| | | VALUES ('mesApiType', 'MESæ¥å£ç±»å', GETDATE(), 'System'); |
| | | SET @DictId = SCOPE_IDENTITY(); |
| | | END |
| | | |
| | | -- æ·»å æ¥å£ç±»åé项 |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) |
| | | SELECT @DictId, 'çµè¯ç»å®', 'BindContainer', 1, GETDATE(), 'System' |
| | | WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'BindContainer'); |
| | | |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) |
| | | SELECT @DictId, 'çµè¯è§£ç»', 'UnBindContainer', 2, GETDATE(), 'System' |
| | | WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'UnBindContainer'); |
| | | |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) |
| | | SELECT @DictId, 'NG䏿¥', 'ContainerNgReport', 3, GETDATE(), 'System' |
| | | WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'ContainerNgReport'); |
| | | |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) |
| | | SELECT @DictId, 'æçè¿ç«', 'InboundInContainer', 4, GETDATE(), 'System' |
| | | WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'InboundInContainer'); |
| | | |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) |
| | | SELECT @DictId, 'æçåºç«', 'OutboundInContainer', 5, GETDATE(), 'System' |
| | | WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'OutboundInContainer'); |
| | | |
| | | -- è°ç¨ç¶æåå
¸ |
| | | DECLARE @StatusDictId INT; |
| | | SELECT @StatusDictId = Id FROM Dt_Dictionary WHERE DictKey = 'mesApiStatus'; |
| | | |
| | | IF @StatusDictId IS NULL |
| | | BEGIN |
| | | INSERT INTO Dt_Dictionary (DictKey, DictValue, CreateDate, Modifier) |
| | | VALUES ('mesApiStatus', 'MESæ¥å£ç¶æ', GETDATE(), 'System'); |
| | | SET @StatusDictId = SCOPE_IDENTITY(); |
| | | END |
| | | |
| | | -- æ·»å ç¶æé项 |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) |
| | | SELECT @StatusDictId, 'æå', 'true', 1, GETDATE(), 'System' |
| | | WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @StatusDictId AND [Key] = 'true'); |
| | | |
| | | INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) |
| | | SELECT @StatusDictId, '失败', 'false', 2, GETDATE(), 'System' |
| | | WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @StatusDictId AND [Key] = 'false'); |
| | | ``` |
| | | |
| | | ### Step 4: ä¿åæ°æ®åºèæ¬ |
| | | |
| | | ```bash |
| | | # å°ä¸è¿° SQL èæ¬ä¿åå°æä»¶ |
| | | cat > WMS/WIDESEA_WMSServer/Database/Scripts/20260413_MesLogPage_Config.sql << 'EOF' |
| | | -- è¿éç²è´´ä¸é¢ç SQL èæ¬ |
| | | EOF |
| | | ``` |
| | | |
| | | ### Step 5: æäº¤æ°æ®åºèæ¬ |
| | | |
| | | ```bash |
| | | git add WMS/WIDESEA_WMSServer/Database/Scripts/20260413_MesLogPage_Config.sql |
| | | git commit -m "feat(MES): æ·»å MESæ¥å¿é¡µé¢æ°æ®åºé
ç½® |
| | | |
| | | - å建æ§è½ç´¢å¼ |
| | | - æ·»å èåè®°å½ |
| | | - æ·»å æ°æ®åå
¸ï¼æ¥å£ç±»åãç¶æï¼ |
| | | |
| | | Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 11: å端ç¼è¯æµè¯ |
| | | |
| | | ### Step 1: ç¼è¯åç«¯é¡¹ç® |
| | | |
| | | ```bash |
| | | cd WMS/WIDESEA_WMSServer |
| | | dotnet build WIDESEA_WMSServer.sln |
| | | ``` |
| | | |
| | | ### Step 2: æ£æ¥ç¼è¯ç»æ |
| | | |
| | | 颿è¾åºï¼ç¼è¯æåï¼æ é误 |
| | | |
| | | ### Step 3: 妿æé误ï¼ä¿®å¤å¹¶éæ°ç¼è¯ |
| | | |
| | | --- |
| | | |
| | | ## Task 12: å端ç¼è¯æµè¯ |
| | | |
| | | ### Step 1: å®è£
ä¾èµï¼å¦éè¦ï¼ |
| | | |
| | | ```bash |
| | | cd WMS/WIDESEA_WMSClient |
| | | npm install |
| | | ``` |
| | | |
| | | ### Step 2: ç¼è¯åç«¯é¡¹ç® |
| | | |
| | | ```bash |
| | | npm run build |
| | | ``` |
| | | |
| | | ### Step 3: æ£æ¥ç¼è¯ç»æ |
| | | |
| | | 颿è¾åºï¼ç¼è¯æåï¼æ é误 |
| | | |
| | | --- |
| | | |
| | | ## Task 13: æå¨åè½æµè¯ |
| | | |
| | | ### æµè¯æ¸
å |
| | | |
| | | - [ ] **å页æ¥è¯¢**: è½æ£å¸¸æ¾ç¤ºæ¥å¿å表 |
| | | - [ ] **çéåè½**: æ¥å£ç±»åãç¶æãæ¶é´èå´ç鿣叏 |
| | | - [ ] **ç»è®¡å¡ç**: æ¾ç¤ºæ£ç¡®çç»è®¡æ°æ® |
| | | - [ ] **JSON 详æ
**: ç¹å»è¯·æ±/ååºåè½æ¥ç宿´ JSON |
| | | - [ ] **导åºåè½**: è½å¯¼åº CSV æä»¶ |
| | | |
| | | --- |
| | | |
| | | ## Task 14: 代ç 审æ¥ä¸æç»æäº¤ |
| | | |
| | | ### Step 1: æ¥çææåæ´ |
| | | |
| | | ```bash |
| | | git status |
| | | git log --oneline -10 |
| | | ``` |
| | | |
| | | ### Step 2: æç»ç¡®è®¤ |
| | | |
| | | 确认ææä»»å¡å·²å®æï¼åè½æµè¯éè¿ |
| | | |
| | | --- |
| | | |
| | | ## éå½: åèææ¡£ |
| | | |
| | | - è®¾è®¡ææ¡£: `docs/superpowers/specs/2026-04-13-mes-api-log-page-design.md` |
| | | - ç°æç³»ç»æ¥å¿: `WMS/WIDESEA_WMSClient/src/views/system/Sys_Log.vue` |
| | | - ç°ææ©å±: `WMS/WIDESEA_WMSClient/src/extension/system/Sys_Log.jsx` |
| | | - MES å®ä½: `WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs` |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # MES æ¥å£è°ç¨æ¥å¿é¡µé¢è®¾è®¡ææ¡£ |
| | | |
| | | **æ¥æ:** 2026-04-13 |
| | | **ä½è
:** Claude |
| | | **çæ¬:** v0.3 |
| | | **ç¶æ:** å·²æ¹å |
| | | |
| | | --- |
| | | |
| | | ## 1. æ¦è¿° |
| | | |
| | | ### 1.1 ç®æ |
| | | |
| | | å¨ WMS ç³»ç»ä¸æ·»å MES æ¥å£è°ç¨æ¥å¿æ¥ç页é¢ï¼æä¾ç»¼åæ§çæ¥å¿æ¥è¯¢ãç»è®¡å管çåè½ã |
| | | |
| | | ### 1.2 èå´ |
| | | |
| | | - å端ï¼API æ¥å£ãæå¡å±æ©å± |
| | | - åç«¯ï¼æ¥å¿å表页é¢ãç»è®¡å¡çãJSON 详æ
æ¥çå¨ |
| | | - æ°æ®åºï¼èåé
ç½®ãæ°æ®åå
¸ |
| | | |
| | | --- |
| | | |
| | | ## 2. å端设计 |
| | | |
| | | ### 2.1 Controller æ¥å£ |
| | | |
| | | **æä»¶è·¯å¾:** `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Mes/MesLogController.cs` |
| | | |
| | | **注æ:** æ¤æ§å¶å¨ä¸ç»§æ¿ `ApiBaseController`ï¼å 为 MES æ¥å¿æ¯åªè¯»è®°å½ï¼ä¸éè¦å®æ´ç CRUD æä½ãä»
æä¾æ¥è¯¢ãç»è®¡å导åºåè½ã |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | POST | `/api/MesLog/page` | å页æ¥è¯¢ï¼æ¯æææç鿡件 | |
| | | | GET | `/api/MesLog/{id}` | è·ååæ¡æ¥å¿å®æ´ä¿¡æ¯ | |
| | | | GET | `/api/MesLog/statistics` | è·åç»è®¡æ°æ® | |
| | | | POST | `/api/MesLog/export` | å¯¼åºæ¥è¯¢ç»æä¸º Excel (ä½¿ç¨æ¡æ¶å
置导åº) | |
| | | |
| | | ### 2.2 æ°æ®ä¼ è¾å¯¹è±¡ |
| | | |
| | | #### 2.2.1 æ¥è¯¢è¯·æ± DTO |
| | | |
| | | ```csharp |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿æ¥è¯¢è¯·æ±DTO |
| | | /// </summary> |
| | | public class MesLogQueryDto |
| | | { |
| | | /// <summary> |
| | | /// æ¥å£ç±»å: BindContainer, UnBindContainer, ContainerNgReport, InboundInContainer, OutboundInContainer |
| | | /// </summary> |
| | | public string ApiType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåç¶æ: null-å
¨é¨, true-æå, false-失败 |
| | | /// </summary> |
| | | public bool? IsSuccess { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¼å§æ¶é´ï¼å端 dateRange åæ®µæ å°ï¼dateRange[0] â StartTimeï¼ |
| | | /// </summary> |
| | | public DateTime? StartTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç»ææ¶é´ï¼å端 dateRange åæ®µæ å°ï¼dateRange[1] â EndTimeï¼ |
| | | /// </summary> |
| | | public DateTime? EndTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åå»ºäººï¼æä½äººï¼ |
| | | /// </summary> |
| | | public string Creator { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå°èæ¶ï¼æ¯«ç§ï¼ï¼å端 elapsedRange åæ®µæ å°ï¼elapsedRange[0] â MinElapsedMsï¼ |
| | | /// </summary> |
| | | public int? MinElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§èæ¶ï¼æ¯«ç§ï¼ï¼å端 elapsedRange åæ®µæ å°ï¼elapsedRange[1] â MaxElapsedMsï¼ |
| | | /// </summary> |
| | | public int? MaxElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é误信æ¯å
³é®å |
| | | /// </summary> |
| | | public string ErrorKeyword { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±JSONå
³é®å |
| | | /// </summary> |
| | | public string JsonRequestKeyword { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºJSONå
³é®å |
| | | /// </summary> |
| | | public string JsonResponseKeyword { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | #### 2.2.2 ç»è®¡æ°æ® DTO |
| | | |
| | | ```csharp |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿ç»è®¡æ°æ®DTO |
| | | /// </summary> |
| | | public class MesLogStatisticsDto |
| | | { |
| | | /// <summary> |
| | | /// æ»è°ç¨æ¬¡æ° |
| | | /// </summary> |
| | | public int TotalCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåæ¬¡æ° |
| | | /// </summary> |
| | | public int SuccessCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æåçï¼ç¾åæ¯ï¼ |
| | | /// </summary> |
| | | public double SuccessRate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¤±è´¥æ¬¡æ° |
| | | /// </summary> |
| | | public int FailedCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å¹³åèæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public double AvgElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§èæ¶ï¼æ¯«ç§ï¼ |
| | | /// </summary> |
| | | public int MaxElapsedMs { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 仿¥è°ç¨æ¬¡æ° |
| | | /// </summary> |
| | | public int TodayCount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 忥å£ç±»åè°ç¨æ¬¡æ°ç»è®¡ |
| | | /// </summary> |
| | | public Dictionary<string, int> ApiTypeCounts { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | #### 2.2.3 å页ååº DTO |
| | | |
| | | ```csharp |
| | | namespace WIDESEA_DTO.MES |
| | | { |
| | | /// <summary> |
| | | /// MESæ¥å¿å表项DTO |
| | | /// </summary> |
| | | public class MesLogListItemDto |
| | | { |
| | | public long Id { get; set; } |
| | | public string ApiType { get; set; } |
| | | public bool IsSuccess { get; set; } |
| | | public string RequestJsonPreview { get; set; } // å200å符é¢è§ |
| | | public string ResponseJsonPreview { get; set; } // å200å符é¢è§ |
| | | public string ErrorMessage { get; set; } |
| | | public int ElapsedMs { get; set; } |
| | | public DateTime CreateDate { get; set; } |
| | | public string Creator { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// MESæ¥å¿è¯¦æ
DTO |
| | | /// </summary> |
| | | public class MesLogDetailDto : MesLogListItemDto |
| | | { |
| | | public string RequestJson { get; set; } // 宿´JSON |
| | | public string ResponseJson { get; set; } // 宿´JSON |
| | | public DateTime? ModifyDate { get; set; } |
| | | public string Modifier { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 2.3 Service æ¥å£æ©å± |
| | | |
| | | **æä»¶è·¯å¾:** `WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs` |
| | | |
| | | ```csharp |
| | | namespace WIDESEA_IMesService |
| | | { |
| | | public interface IMesLogService : IDependency |
| | | { |
| | | // ç°ææ¹æ³ |
| | | Task<bool> LogAsync(MesApiLogDto log); |
| | | Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50); |
| | | |
| | | // æ°å¢æ¹æ³ |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | Task<(List<MesLogListItemDto> items, int total)> GetPageAsync(MesLogQueryDto query, int page, int pageSize); |
| | | |
| | | /// <summary> |
| | | /// è·ååæ¡æ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | Task<MesLogDetailDto> GetDetailAsync(long id); |
| | | |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | Task<MesLogStatisticsDto> GetStatisticsAsync(MesLogQueryDto query); |
| | | |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿æ°æ® |
| | | /// </summary> |
| | | Task<byte[]> ExportAsync(MesLogQueryDto query); |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 2.4 Controller å®ç°æ¡æ¶ |
| | | |
| | | ```csharp |
| | | namespace WIDESEA_WMSServer.Controllers.Mes |
| | | { |
| | | [Route("api/MesLog")] |
| | | [ApiController] |
| | | public class MesLogController : ControllerBase |
| | | { |
| | | private readonly IMesLogService _mesLogService; |
| | | |
| | | public MesLogController(IMesLogService mesLogService) |
| | | { |
| | | _mesLogService = mesLogService; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å页æ¥è¯¢MESæ¥å¿ |
| | | /// </summary> |
| | | [HttpPost("page")] |
| | | public async Task<WebResponseContent> GetPage([FromBody] MesLogQueryDto query, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) |
| | | { |
| | | // å®ç°å页æ¥è¯¢ |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæ¥å¿è¯¦æ
|
| | | /// </summary> |
| | | [HttpGet("{id}")] |
| | | public async Task<WebResponseContent> GetDetail(long id) |
| | | { |
| | | // å®ç°è¯¦æ
æ¥è¯¢ |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åç»è®¡æ°æ® |
| | | /// </summary> |
| | | [HttpGet("statistics")] |
| | | public async Task<WebResponseContent> GetStatistics([FromQuery] MesLogQueryDto query) |
| | | { |
| | | // å®ç°ç»è®¡æ¥è¯¢ |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¯¼åºæ¥å¿ - ä½¿ç¨æ¡æ¶å
ç½® ServiceBase.Export() æ¹æ³ |
| | | /// éè¿ HttpHelper.Post() è°ç¨æ¡æ¶çéç¨å¯¼åºæ¥å£ |
| | | /// </summary> |
| | | [HttpPost("export")] |
| | | public async Task<IActionResult> Export([FromBody] MesLogQueryDto query) |
| | | { |
| | | // è°ç¨ _mesLogService.ExportAsync() çææ°æ® |
| | | // ä½¿ç¨æ¡æ¶å
ç½®ç ExcelExporter å¯¼åº |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 2.5 æå¡æ³¨å |
| | | |
| | | **注æ:** `IMesLogService` å·²å®ç° `IDependency` æ¥å£ï¼æ éæå¨æ³¨åãæ¡æ¶ä¼éè¿ `AutofacModuleRegister` èªå¨æ³¨åææ `IDependency` å®ç°ã |
| | | |
| | | --- |
| | | |
| | | ## 3. å端设计 |
| | | |
| | | ### 3.1 æä»¶ç»æ |
| | | |
| | | ``` |
| | | src/ |
| | | âââ views/ |
| | | â âââ system/ |
| | | â âââ Mes_Log.vue # ä¸»é¡µé¢ |
| | | âââ extension/ |
| | | â âââ system/ |
| | | â âââ Mes_Log.jsx # ä¸å¡æ©å±é»è¾ |
| | | âââ components/ |
| | | â âââ MesJsonViewer.vue # JSON 详æ
æ¥çå¨ç»ä»¶ |
| | | âââ router/ |
| | | âââ viewGird.js # æ·»å è·¯ç±é
ç½® |
| | | ``` |
| | | |
| | | ### 3.2 Mes_Log.vue é
ç½® |
| | | |
| | | **注æ:** `view-grid` ç»ä»¶ä¸æ¯æ `toolbar` ææ§½ï¼ç»è®¡å¡çéè¦æ¾å¨ `view-grid` å¤é¨ã |
| | | |
| | | ```vue |
| | | <template> |
| | | <div class="mes-log-page"> |
| | | <!-- ç»è®¡å¡çåºåï¼ä½äº view-grid 䏿¹ï¼ --> |
| | | <mes-log-statistics ref="statistics" @refresh="onStatsRefresh" /> |
| | | |
| | | <!-- æ¥å¿å表 --> |
| | | <view-grid |
| | | ref="grid" |
| | | :columns="columns" |
| | | :detail="detail" |
| | | :editFormFields="editFormFields" |
| | | :editFormOptions="editFormOptions" |
| | | :searchFormFields="searchFormFields" |
| | | :searchFormOptions="searchFormOptions" |
| | | :table="table" |
| | | :extend="extend" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import extend from "@/extension/system/Mes_Log.jsx"; |
| | | import { ref, defineComponent } from "vue"; |
| | | |
| | | export default defineComponent({ |
| | | setup() { |
| | | const table = ref({ |
| | | key: "Id", |
| | | cnName: "MESæ¥å£æ¥å¿", |
| | | name: "Mes_Log", |
| | | url: "/api/MesLog/", |
| | | sortName: "Id DESC", |
| | | }); |
| | | |
| | | const columns = ref([ |
| | | { field: "id", title: "ID", width: 80, hidden: true }, |
| | | { |
| | | field: "apiType", |
| | | title: "æ¥å£ç±»å", |
| | | width: 130, |
| | | bind: { key: "mesApiType", data: [] } |
| | | }, |
| | | { |
| | | field: "isSuccess", |
| | | title: "ç¶æ", |
| | | width: 80, |
| | | bind: { key: "mesApiStatus", data: [] } |
| | | }, |
| | | { |
| | | field: "requestJson", |
| | | title: "请æ±å
容", |
| | | width: 200, |
| | | link: true, |
| | | formatter: (row) => previewJson(row.requestJson) |
| | | }, |
| | | { |
| | | field: "responseJson", |
| | | title: "ååºå
容", |
| | | width: 200, |
| | | link: true, |
| | | formatter: (row) => previewJson(row.responseJson) |
| | | }, |
| | | { field: "errorMessage", title: "é误信æ¯", width: 200 }, |
| | | { field: "elapsedMs", title: "èæ¶(ms)", width: 100, sortable: true }, |
| | | { field: "createDate", title: "è°ç¨æ¶é´", width: 160, sortable: true }, |
| | | { field: "creator", title: "æä½äºº", width: 100 } |
| | | ]); |
| | | |
| | | // JSON å
容é¢è§è¾
å©å½æ°ï¼å¨ Mes_Log.jsx ä¸å®ç°ï¼ |
| | | const previewJson = (jsonStr) => { |
| | | if (!jsonStr) return '-'; |
| | | try { |
| | | const obj = JSON.parse(jsonStr); |
| | | return JSON.stringify(obj, null, 2).substring(0, 200) + '...'; |
| | | } catch { |
| | | return String(jsonStr).substring(0, 200) + '...'; |
| | | } |
| | | }; |
| | | |
| | | const searchFormOptions = ref([ |
| | | [ |
| | | { field: "apiType", title: "æ¥å£ç±»å", type: "select" }, |
| | | { field: "isSuccess", title: "ç¶æ", type: "select" }, |
| | | { field: "dateRange", title: "æ¶é´èå´", type: "datetimeRange" } |
| | | ], |
| | | [ |
| | | { field: "creator", title: "æä½äºº", type: "text" }, |
| | | { |
| | | field: "elapsedRange", |
| | | title: "èæ¶èå´(ms)", |
| | | type: "numberRange", |
| | | placeholder: ["æå°", "æå¤§"] |
| | | } |
| | | ], |
| | | [ |
| | | { field: "errorKeyword", title: "é误å
³é®å", type: "text" }, |
| | | { field: "jsonKeyword", title: "JSONå
容å
³é®å", type: "text" } |
| | | ] |
| | | ]); |
| | | |
| | | // ... å
¶ä»é
ç½® |
| | | |
| | | return { table, columns, searchFormOptions, extend, /* ... */ }; |
| | | } |
| | | }); |
| | | </script> |
| | | ``` |
| | | |
| | | ### 3.3 MesJsonViewer.vue ç»ä»¶ |
| | | |
| | | **注æ:** ä¸ä½¿ç¨å¤é¨ JSON æ¥çå¨åºï¼éç¨åç `<pre>` æ ç¾ + `JSON.stringify()` æ ¼å¼åæ¾ç¤ºã |
| | | |
| | | ```vue |
| | | <template> |
| | | <el-dialog |
| | | v-model="visible" |
| | | :title="title" |
| | | width="800px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-tabs v-model="activeTab"> |
| | | <el-tab-pane label="请æ±" name="request"> |
| | | <div class="json-container"> |
| | | <el-button |
| | | size="small" |
| | | class="copy-btn" |
| | | @click="copyToClipboard(formattedRequest)" |
| | | > |
| | | å¤å¶ |
| | | </el-button> |
| | | <pre class="json-content">{{ formattedRequest }}</pre> |
| | | </div> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="ååº" name="response"> |
| | | <div class="json-container"> |
| | | <el-button |
| | | size="small" |
| | | class="copy-btn" |
| | | @click="copyToClipboard(formattedResponse)" |
| | | > |
| | | å¤å¶ |
| | | </el-button> |
| | | <pre class="json-content">{{ formattedResponse }}</pre> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | |
| | | <template #footer> |
| | | <el-button @click="visible = false">å
³é</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | export default { |
| | | name: "MesJsonViewer", |
| | | props: { |
| | | modelValue: Boolean, |
| | | title: String, |
| | | requestJson: [String, Object], |
| | | responseJson: [String, Object] |
| | | }, |
| | | emits: ["update:modelValue"], |
| | | setup(props) { |
| | | const activeTab = ref('request'); |
| | | |
| | | const formattedRequest = computed(() => { |
| | | try { |
| | | const obj = typeof props.requestJson === 'string' |
| | | ? JSON.parse(props.requestJson) |
| | | : props.requestJson; |
| | | return JSON.stringify(obj, null, 2); |
| | | } catch { |
| | | return props.requestJson || '{}'; |
| | | } |
| | | }); |
| | | |
| | | const formattedResponse = computed(() => { |
| | | try { |
| | | const obj = typeof props.responseJson === 'string' |
| | | ? JSON.parse(props.responseJson) |
| | | : props.responseJson; |
| | | return JSON.stringify(obj, null, 2); |
| | | } catch { |
| | | return props.responseJson || '{}'; |
| | | } |
| | | }); |
| | | |
| | | const copyToClipboard = (text) => { |
| | | // ä¼å
使ç¨ç°ä»£ Clipboard APIï¼éè¦ HTTPS æ localhostï¼ |
| | | if (navigator.clipboard && navigator.clipboard.writeText) { |
| | | navigator.clipboard.writeText(text).catch(() => { |
| | | // å¦æå¤±è´¥ï¼ä½¿ç¨éçº§æ¹æ¡ |
| | | fallbackCopy(text); |
| | | }); |
| | | } else { |
| | | // éçº§æ¹æ¡ï¼å
¼å®¹ HTTP ç¯å¢åæ§æµè§å¨ |
| | | fallbackCopy(text); |
| | | } |
| | | }; |
| | | |
| | | const fallbackCopy = (text) => { |
| | | const textarea = document.createElement('textarea'); |
| | | textarea.value = text; |
| | | textarea.style.position = 'fixed'; |
| | | textarea.style.opacity = '0'; |
| | | document.body.appendChild(textarea); |
| | | textarea.select(); |
| | | try { |
| | | document.execCommand('copy'); |
| | | } catch (err) { |
| | | console.error('å¤å¶å¤±è´¥:', err); |
| | | } |
| | | document.body.removeChild(textarea); |
| | | }; |
| | | |
| | | return { |
| | | visible: props.modelValue, |
| | | activeTab, |
| | | formattedRequest, |
| | | formattedResponse, |
| | | copyToClipboard |
| | | }; |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .json-container { |
| | | position: relative; |
| | | } |
| | | |
| | | .copy-btn { |
| | | position: absolute; |
| | | top: 8px; |
| | | right: 8px; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .json-content { |
| | | background: #f5f7fa; |
| | | padding: 16px; |
| | | border-radius: 4px; |
| | | max-height: 500px; |
| | | overflow: auto; |
| | | font-size: 12px; |
| | | line-height: 1.5; |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | ### 3.4 ç»è®¡å¡çç»ä»¶ |
| | | |
| | | **æä»¶è·¯å¾:** `src/components/MesLogStatistics.vue` |
| | | |
| | | **注æ:** å½åé¡¹ç® Element Plus çæ¬ä¸º 2.2.14ï¼ä¸æ¯æ `el-statistic` ç»ä»¶ï¼éè¦ 2.3+ï¼ãéç¨èªå®ä¹å¡çå®ç°ã |
| | | |
| | | ```vue |
| | | <template> |
| | | <div class="mes-log-statistics"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-primary"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">æ»è°ç¨æ¬¡æ°</div> |
| | | <div class="stat-value">{{ statistics.totalCount }}</div> |
| | | <div class="stat-unit">次</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card" :class="statistics.successRate >= 90 ? 'stat-success' : 'stat-warning'"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">æåç</div> |
| | | <div class="stat-value">{{ statistics.successRate }}%</div> |
| | | <div class="stat-sub"> |
| | | æå: {{ statistics.successCount }} / 失败: {{ statistics.failedCount }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-info"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">å¹³åèæ¶</div> |
| | | <div class="stat-value">{{ Math.round(statistics.avgElapsedMs) }}</div> |
| | | <div class="stat-unit">ms</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover" class="stat-card stat-secondary"> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">仿¥è°ç¨</div> |
| | | <div class="stat-value">{{ statistics.todayCount }}</div> |
| | | <div class="stat-unit">次</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ref, onMounted } from 'vue'; |
| | | import axios from '@/api/axios'; |
| | | |
| | | export default { |
| | | name: "MesLogStatistics", |
| | | setup() { |
| | | const statistics = ref({ |
| | | totalCount: 0, |
| | | successCount: 0, |
| | | failedCount: 0, |
| | | successRate: 0, |
| | | avgElapsedMs: 0, |
| | | todayCount: 0 |
| | | }); |
| | | |
| | | const fetchStatistics = async () => { |
| | | const res = await axios.post('/api/MesLog/statistics', {}); |
| | | if (res.status) { |
| | | statistics.value = res.data; |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchStatistics(); |
| | | }); |
| | | |
| | | return { statistics, fetchStatistics }; |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .mes-log-statistics { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .stat-card { |
| | | text-align: center; |
| | | } |
| | | |
| | | .stat-content { |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 28px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .stat-sub { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .stat-primary .stat-value { color: #409EFF; } |
| | | .stat-success .stat-value { color: #67C23A; } |
| | | .stat-warning .stat-value { color: #E6A23C; } |
| | | .stat-info .stat-value { color: #909399; } |
| | | .stat-secondary .stat-value { color: #909399; } |
| | | </style> |
| | | ``` |
| | | |
| | | ### 3.5 èªå¨å·æ°åè½ |
| | | |
| | | **UX 说æ:** |
| | | - å·æ°æ§å¶ä½äºé¡µé¢å³ä¸è§ï¼ä¸æ¥è¯¢æ¡ä»¶å¹¶å |
| | | - å½ç¨æ·æå¼çé颿¿ãç¼è¾æ¥è¯¢æ¡ä»¶ææ¥ç详æ
æ¶ï¼èªå¨å·æ°æå |
| | | - ç¨æ·æå¨ç¹å»æ¥è¯¢/å·æ°æé®åï¼èªå¨å·æ°éæ°è®¡æ¶ |
| | | |
| | | ```javascript |
| | | // å¨ Mes_Log.jsx ä¸å®ç° |
| | | const refreshOptions = [ |
| | | { label: "å
³é", value: 0 }, |
| | | { label: "10ç§", value: 10 }, |
| | | { label: "30ç§", value: 30 }, |
| | | { label: "1åé", value: 60 }, |
| | | { label: "5åé", value: 300 } |
| | | ]; |
| | | |
| | | let refreshTimer = null; |
| | | |
| | | const setAutoRefresh = (interval) => { |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | refreshTimer = null; |
| | | } |
| | | if (interval > 0) { |
| | | refreshTimer = setInterval(() => { |
| | | // ä»
å¨ç¨æ·æªäº¤äºæ¶å·æ°åè¡¨æ°æ® |
| | | if (!isUserInteracting) { |
| | | refresh(); |
| | | } |
| | | }, interval * 1000); |
| | | } |
| | | }; |
| | | |
| | | // ç¨æ·äº¤äºæ¶æåå·æ° |
| | | const onUserInteractionStart = () => { |
| | | isUserInteracting = true; |
| | | }; |
| | | |
| | | const onUserInteractionEnd = () => { |
| | | isUserInteracting = false; |
| | | }; |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 4. æ°æ®åå
¸é
ç½® |
| | | |
| | | ### 4.1 æ¥å£ç±»ååå
¸ (mesApiType) |
| | | |
| | | | å¼ | æ¾ç¤ºææ¬ | 说æ | |
| | | |---|---------|------| |
| | | | BindContainer | çµè¯ç»å® | æççµè¯ç»å®æä½ | |
| | | | UnBindContainer | çµè¯è§£ç» | æççµè¯è§£ç»æä½ | |
| | | | ContainerNgReport | NG䏿¥ | æçNGçµè¯ä¸æ¥ | |
| | | | InboundInContainer | æçè¿ç« | æçè¿ç«æä½ | |
| | | | OutboundInContainer | æçåºç« | æçåºç«æä½ | |
| | | |
| | | ### 4.2 è°ç¨ç¶æåå
¸ (mesApiStatus) |
| | | |
| | | | å¼ | æ¾ç¤ºææ¬ | é¢è² | |
| | | |---|---------|------| |
| | | | true | æå | ç»¿è² | |
| | | | false | 失败 | çº¢è² | |
| | | |
| | | --- |
| | | |
| | | ## 5. æéä¸èå |
| | | |
| | | ### 5.1 èåé
ç½® |
| | | |
| | | å¨ `Dt_Menu` è¡¨ä¸æ·»å ï¼ |
| | | |
| | | | åæ®µ | å¼ | |
| | | |------|-----| |
| | | | ParentId | (ç³»ç»ç®¡çèåç ID) | |
| | | | MenuName | MESæ¥å£æ¥å¿ | |
| | | | Url | /Mes_Log | |
| | | | Component | views/system/Mes_Log | |
| | | | Permission | Mes_Log:view | |
| | | | Sort | (æå¨ Sys_Log ä¹å) | |
| | | | Icon | el-icon-document | |
| | | |
| | | ### 5.2 æéç¹ |
| | | |
| | | - `Mes_Log:view` - æ¥çæ¥å¿å表 |
| | | - `Mes_Log:detail` - æ¥çæ¥å¿è¯¦æ
|
| | | - `Mes_Log:export` - å¯¼åºæ¥å¿ |
| | | |
| | | --- |
| | | |
| | | ## 6. å®ç°æ¥éª¤ |
| | | |
| | | ### 6.1 å端å®ç° |
| | | |
| | | 1. æ©å± `IMesLogService` æ¥å£ï¼æ·»å å页ãç»è®¡ãå¯¼åºæ¹æ³ï¼ |
| | | 2. å®ç° `MesLogService` çæ©å±æ¹æ³ |
| | | 3. å建 DTO æä»¶ï¼`MesLogQueryDto.cs`, `MesLogStatisticsDto.cs`, `MesLogListItemDto.cs`ï¼ |
| | | 4. å建 `MesLogController.cs` |
| | | 5. **注æ:** æå¡æ³¨åèªå¨å®æï¼`IMesLogService` å·²å®ç° `IDependency`ï¼ |
| | | |
| | | ### 6.2 å端å®ç° |
| | | |
| | | 1. å建 `MesJsonViewer.vue` ç»ä»¶ï¼ä½¿ç¨åç `<pre>` æ ç¾ï¼æ å¤é¨ä¾èµï¼ |
| | | 2. å建 `MesLogStatistics.vue` ç»ä»¶ï¼ä½¿ç¨ `el-card` èªå®ä¹å®ç°ï¼ |
| | | 3. å建 `Mes_Log.jsx` æ©å±é»è¾ |
| | | 4. å建 `Mes_Log.vue` é¡µé¢ |
| | | 5. å¨ `viewGird.js` æ·»å è·¯ç± |
| | | 6. å¨ `extension` ç®å½æ·»å æ©å±æä»¶ |
| | | |
| | | ### 6.3 æ°æ®åºé
ç½® |
| | | |
| | | 1. 确认 `Dt_MesApiLog` 表已åå¨ï¼åè§ `Database/Scripts/20260412_MesApiLog.sql`ï¼ |
| | | 2. åå»ºæ°æ®åºç´¢å¼ï¼åè§ 6.4 èï¼ |
| | | 3. æå
¥èåè®°å½å° `Dt_Menu` 表 |
| | | 4. æå
¥æ°æ®åå
¸è®°å½å° `Dt_Dictionary` 表 |
| | | 5. åé
æéç»è§è² |
| | | |
| | | ### 6.4 æ°æ®åºç´¢å¼ |
| | | |
| | | æ§è¡ä»¥ä¸ SQL å建索å¼ä»¥ä¼åæ¥è¯¢æ§è½ï¼ |
| | | |
| | | ```sql |
| | | -- æ¥å£ç±»åç´¢å¼ï¼ç¨äºæç±»åçéï¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_ApiType' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_ApiType ON Dt_MesApiLog(ApiType); |
| | | END |
| | | |
| | | -- å建æ¶é´ç´¢å¼ï¼ç¨äºæ¶é´èå´æ¥è¯¢åæåºï¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_CreateDate' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_CreateDate ON Dt_MesApiLog(CreateDate DESC); |
| | | END |
| | | |
| | | -- æåç¶æç´¢å¼ï¼ç¨äºæå/失败çéï¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_IsSuccess' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_IsSuccess ON Dt_MesApiLog(IsSuccess); |
| | | END |
| | | |
| | | -- å建人索å¼ï¼ç¨äºææä½äººçéï¼ |
| | | IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_Creator' AND object_id = OBJECT_ID('Dt_MesApiLog')) |
| | | BEGIN |
| | | CREATE INDEX IX_MesApiLog_Creator ON Dt_MesApiLog(Creator); |
| | | END |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 7. ææ¯è¦ç¹ |
| | | |
| | | ### 7.1 JSON å
容æç´¢ |
| | | |
| | | ç±äº SQL Server 对 JSON åæ®µæ¯ææéï¼éç¨ `LIKE` æç´¢ï¼ |
| | | - éç¨äºå
³é®åæç´¢ |
| | | - æ³¨ææ§è½å½±åï¼å»ºè®®é
åæ¶é´èå´çé |
| | | - æç´¢å段ï¼`RequestJson`ã`ResponseJson`ã`ErrorMessage` |
| | | |
| | | ### 7.2 å页æ§è½ |
| | | |
| | | - å©ç¨ç´¢å¼ï¼åè§ 6.4 èæ°æ®åºç´¢å¼ï¼ |
| | | - å¤§æ°æ®éæ¶å»ºè®®å¢å æ¶é´èå´éå¶ï¼é»è®¤æ¾ç¤ºæè¿ 7 å¤©ï¼ |
| | | - èèæ·»å `TOP 1000` éå¶é²æ¢å
¨è¡¨æ«æ |
| | | |
| | | ### 7.3 导åºåè½ |
| | | |
| | | - ä½¿ç¨æ¡æ¶å
ç½®ç `ServiceBase.Export()` æ¹æ³ |
| | | - éè¿ `Magicodes.ExporterAndImporter.Excel` åºå®ç° |
| | | - å¯¼åºæä»¶å½åï¼`MESæ¥å£æ¥å¿_YYYYMMDD_HHMMSS.xlsx` |
| | | - æ¯æä¸å½åæ¥è¯¢æ¡ä»¶ä¸è´çå¯¼åº |
| | | |
| | | ### 7.4 åç«¯åæ®µæ å° |
| | | |
| | | | åç«¯åæ®µ | å端 DTO åæ®µ | 说æ | |
| | | |---------|--------------|------| |
| | | | dateRange[0] | StartTime | å¼å§æ¶é´ | |
| | | | dateRange[1] | EndTime | ç»ææ¶é´ | |
| | | | elapsedRange[0] | MinElapsedMs | æå°èæ¶ | |
| | | | elapsedRange[1] | MaxElapsedMs | æå¤§èæ¶ | |
| | | | jsonKeyword | JsonRequestKeyword, JsonResponseKeyword | åæ¶æç´¢è¯·æ±åååº | |
| | | |
| | | --- |
| | | |
| | | ## 8. æµè¯è¦ç¹ |
| | | |
| | | 1. **å页æ¥è¯¢** - éªè¯åç§ç鿡件çç»å |
| | | 2. **ç»è®¡åç¡®æ§** - éªè¯ç»è®¡æ°æ®ä¸å®é
æ°æ®ä¸è´ |
| | | 3. **JSONå±ç¤º** - éªè¯æ ¼å¼åãæå ãå¤å¶åè½ |
| | | 4. **èªå¨å·æ°** - éªè¯å®æ¶å·æ°åè½æ£å¸¸å·¥ä½ |
| | | 5. **导åºåè½** - éªè¯ Excel æä»¶å
容æ£ç¡® |
| | | 6. **æéæ§å¶** - éªè¯æ æéç¨æ·æ æ³è®¿é® |
| | | |
| | | --- |
| | | |
| | | ## 9. åç»ä¼å建议 |
| | | |
| | | 1. **æ¥å¿å½æ¡£** - èè宿彿¡£æ§æ¥å¿ï¼ä¿æä¸»è¡¨æ§è½ |
| | | 2. **宿¶çæ§** - éæ SignalR å®ç°å®æ¶æ¥å¿æ¨é |
| | | 3. **å¼å¸¸åè¦** - 失败çè¶
è¿é弿¶åéåè¦ |
| | | 4. **æ§è½åæ** - æ·»å èæ¶è¶å¿å¾è¡¨ |
| | | 5. **æ¥å£å¯¹æ¯** - æ¯æåç±»æ¥å£è°ç¨ç对æ¯åæ |