guoziyun 1 maand geleden
bovenliggende
commit
5bc576d880

+ 8 - 13
oms/dist/src/services/userService.js

@@ -47,8 +47,7 @@ class UserService {
             if (updateData.uid) {
                 delete updateData.uid;
             }
-            const updatedUser = await userModel_1.User.findOneAndUpdate({ uid: uid }, updateData, { new: true } // 返回更新后的文档
-            );
+            const updatedUser = await userModel_1.User.findOneAndUpdate({ uid: uid }, updateData, { new: true });
             return updatedUser;
         }
         catch (error) {
@@ -81,17 +80,13 @@ class UserService {
     async getPaginatedUsers(page, limit, query) {
         try {
             const skip = (page - 1) * limit;
-            // 异步执行查询和计数
-            const [users, total] = await Promise.all([
-                // 👈 关键修改:将 query 断言为 FilterQuery<IUser>
-                userModel_1.User.find(query)
-                    .sort({ createdAt: -1 })
-                    .skip(skip)
-                    .limit(limit)
-                    .exec(),
-                userModel_1.User.countDocuments(query).exec(),
-            ]);
-            return { users, total };
+            const filterQuery = query;
+            const hasFilter = Object.keys(filterQuery).length > 0;
+            // 无过滤条件时用 estimatedDocumentCount() 避免全表 count
+            const countPromise = hasFilter ? userModel_1.User.countDocuments(filterQuery).exec() : userModel_1.User.estimatedDocumentCount().exec();
+            // 按 _id 降序(ObjectId 自带时间单调性且有主键索引),加 lean() 减少内存开销
+            const [users, total] = await Promise.all([userModel_1.User.find(filterQuery).sort({ _id: -1 }).skip(skip).limit(limit).lean().exec(), countPromise]);
+            return { users: users, total };
         }
         catch (error) {
             console.error("获取分页用户列表时出错:", error);

+ 1 - 1
oms/public/app/index.html

@@ -9,5 +9,5 @@
   <style>body,html{width:100%;height:100%}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}body{margin:0;color:#000000d9;font-size:14px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-variant:tabular-nums;line-height:1.5715;background-color:#fff;font-feature-settings:"tnum"}html{--antd-wave-shadow-color:#1890ff;--scroll-bar:0}</style><link rel="stylesheet" href="styles-LXBSU6DF.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-LXBSU6DF.css"></noscript></head>
   <body>
     <app-root></app-root>
-  <script src="polyfills-B6TNHZQ6.js" type="module"></script><script src="main-SCPRHGAU.js" type="module"></script></body>
+  <script src="polyfills-B6TNHZQ6.js" type="module"></script><script src="main-GYK3YHCE.js" type="module"></script></body>
 </html>

File diff suppressed because it is too large
+ 0 - 0
oms/public/app/main-GYK3YHCE.js


+ 9 - 12
oms/src/services/userService.ts

@@ -51,7 +51,7 @@ class UserService {
       const updatedUser = await User.findOneAndUpdate(
         { uid: uid },
         updateData,
-        { new: true } // 返回更新后的文档
+        { new: true }, // 返回更新后的文档
       );
       return updatedUser;
     } catch (error) {
@@ -85,19 +85,16 @@ class UserService {
   public async getPaginatedUsers(page: number, limit: number, query: Partial<IUser>): Promise<{ users: IUser[]; total: number }> {
     try {
       const skip = (page - 1) * limit;
+      const filterQuery = query as FilterQuery<IUser>;
+      const hasFilter = Object.keys(filterQuery).length > 0;
 
-      // 异步执行查询和计数
-      const [users, total] = await Promise.all([
-        // 👈 关键修改:将 query 断言为 FilterQuery<IUser>
-        User.find(query as FilterQuery<IUser>)
-          .sort({ createdAt: -1 })
-          .skip(skip)
-          .limit(limit)
-          .exec(),
-        User.countDocuments(query as FilterQuery<IUser>).exec(),
-      ]);
+      // 无过滤条件时用 estimatedDocumentCount() 避免全表 count
+      const countPromise = hasFilter ? User.countDocuments(filterQuery).exec() : User.estimatedDocumentCount().exec();
 
-      return { users, total };
+      // 按 _id 降序(ObjectId 自带时间单调性且有主键索引),加 lean() 减少内存开销
+      const [users, total] = await Promise.all([User.find(filterQuery).sort({ _id: -1 }).skip(skip).limit(limit).lean().exec(), countPromise]);
+
+      return { users: users as unknown as IUser[], total };
     } catch (error) {
       console.error("获取分页用户列表时出错:", error);
       throw new Error("无法检索用户列表。");

+ 14 - 4
omsapp/src/app/pages/art-done-rate.component.ts

@@ -17,7 +17,9 @@ type ArtDoneRateData = {
       <span class="rate">{{ format(absRatePercent) }}%</span>
       <span *ngIf="diff > 0" class="trend">↑ {{ format(diff) }}%</span>
       <span *ngIf="diff < 0" class="trend">↓ {{ format(diff) }}%</span>
-      <span *ngIf="!simple" class="meta">({{ doneCount }}/{{ startCount }})</span>
+      <span *ngIf="!simple" class="meta"
+        >({{ doneCount }}/{{ startCount }})</span
+      >
     </div>
   `,
   styles: [
@@ -68,7 +70,12 @@ export class ArtDoneRateComponent {
 
   get diff(): number {
     const floor = Number(this.item?.areaCountFloor || 0);
-    if (!Number.isFinite(floor) || floor < 0 || floor >= averageDoneRates.length) return 0;
+    if (
+      !Number.isFinite(floor) ||
+      floor < 0 ||
+      floor >= averageDoneRates.length
+    )
+      return 0;
     return this.absRatePercent - averageDoneRates[floor];
   }
 
@@ -85,7 +92,10 @@ export class ArtDoneRateComponent {
   }
 }
 
-const averageDoneRates: number[] = [87.78, 84.69, 77.44, 74.39, 73.31, 73.01, 71.45, 68.00, 66.75, 64.99, 63.64, 64.12, 63.93, 62.26, 57.40];
+const averageDoneRates: number[] = [
+  87.78, 84.69, 77.44, 74.39, 73.31, 73.01, 71.45, 68.0, 66.75, 64.99, 63.64,
+  64.12, 63.93, 62.26, 57.4,
+];
 
 /*
 Loading file: area_count_done_rate.js
@@ -104,4 +114,4 @@ Loading file: area_count_done_rate.js
 区块数区间: 1200-1300, 完成率: 63.93%
 区块数区间: 1300-1400, 完成率: 62.26%
 区块数区间: 1400-1500, 完成率: 57.40%
-*/
+*/

+ 78 - 22
omsapp/src/app/pages/dashboard.component.ts

@@ -35,15 +35,26 @@ import { NzMessageService } from 'ng-zorro-antd/message';
       <nz-page-header [nzGhost]="false">
         <nz-page-header-title>数据看板</nz-page-header-title>
         <nz-page-header-extra>
-          <span nz-icon nzType="sync" nzTheme="outline" (click)="refreshData()"></span>
+          <span
+            nz-icon
+            nzType="sync"
+            nzTheme="outline"
+            (click)="refreshData()"
+          ></span>
         </nz-page-header-extra>
         <nz-page-header-content>
-          <p>最后更新时间:{{ lastUpdateTime | date: 'yyyy-MM-dd HH:mm:ss' }}</p>
+          <p>
+            最后更新时间:{{ lastUpdateTime | date: 'yyyy-MM-dd HH:mm:ss' }}
+          </p>
         </nz-page-header-content>
       </nz-page-header>
 
       <nz-spin [nzSpinning]="isLoading" nzTip="数据加载中...">
-        <nz-card nzTitle="用户卡片:当日日活 + 日活曲线" nzHoverable class="stack-card">
+        <nz-card
+          nzTitle="用户卡片:当日日活 + 日活曲线"
+          nzHoverable
+          class="stack-card"
+        >
           <nz-statistic
             [nzTitle]="'当日日活(DAU)'"
             [nzValue]="activeUsersToday"
@@ -56,13 +67,38 @@ import { NzMessageService } from 'ng-zorro-antd/message';
           </ng-template>
 
           <div class="range-buttons">
-            <button type="button" class="range-btn" [class.active]="selectedDauRange === 7" (click)="changeDauRange(7)">7天</button>
-            <button type="button" class="range-btn" [class.active]="selectedDauRange === 14" (click)="changeDauRange(14)">14天</button>
-            <button type="button" class="range-btn" [class.active]="selectedDauRange === 30" (click)="changeDauRange(30)">30天</button>
+            <button
+              type="button"
+              class="range-btn"
+              [class.active]="selectedDauRange === 7"
+              (click)="changeDauRange(7)"
+            >
+              7天
+            </button>
+            <button
+              type="button"
+              class="range-btn"
+              [class.active]="selectedDauRange === 14"
+              (click)="changeDauRange(14)"
+            >
+              14天
+            </button>
+            <button
+              type="button"
+              class="range-btn"
+              [class.active]="selectedDauRange === 30"
+              (click)="changeDauRange(30)"
+            >
+              30天
+            </button>
           </div>
 
           <div class="chart-wrapper">
-            <svg viewBox="0 0 600 180" class="line-chart" preserveAspectRatio="none">
+            <svg
+              viewBox="0 0 600 180"
+              class="line-chart"
+              preserveAspectRatio="none"
+            >
               <polyline
                 *ngIf="dauChartPoints"
                 [attr.points]="dauChartPoints"
@@ -113,10 +149,19 @@ import { NzMessageService } from 'ng-zorro-antd/message';
           </div>
         </nz-card>
 
-        <nz-card nzTitle="作品卡片:最近7天上新作品表现" nzHoverable class="stack-card">
-          <nz-tabset [nzSelectedIndex]="activeArtworkTabIndex" (nzSelectedIndexChange)="activeArtworkTabIndex = $event">
+        <nz-card
+          nzTitle="作品卡片:最近7天上新作品表现"
+          nzHoverable
+          class="stack-card"
+        >
+          <nz-tabset
+            [nzSelectedIndex]="activeArtworkTabIndex"
+            (nzSelectedIndexChange)="activeArtworkTabIndex = $event"
+          >
             <nz-tab *ngFor="let tab of artworkTabs" [nzTitle]="tab.label">
-              <div class="tab-subtitle">{{ tab.date }} · 当日DAU {{ tab.dau }}</div>
+              <div class="tab-subtitle">
+                {{ tab.date }} · 当日DAU {{ tab.dau }}
+              </div>
 
               <nz-table
                 #worksTable
@@ -148,7 +193,11 @@ import { NzMessageService } from 'ng-zorro-antd/message';
                       />
                     </td>
                     <td>
-                      <a [href]="getArtworkDetailUrl(work.resId)" target="_blank" rel="noopener noreferrer">
+                      <a
+                        [href]="getArtworkDetailUrl(work.resId)"
+                        target="_blank"
+                        rel="noopener noreferrer"
+                      >
                         {{ work.name }}
                       </a>
                     </td>
@@ -160,7 +209,7 @@ import { NzMessageService } from 'ng-zorro-antd/message';
                           completionRate: work.completionRate,
                           areaCountFloor: work.areaCountFloor,
                           totalStartCount: work.clickCount,
-                          totalDoneCount: work.completionCount
+                          totalDoneCount: work.completionCount,
                         }"
                       ></art-done-rate>
                     </td>
@@ -171,7 +220,9 @@ import { NzMessageService } from 'ng-zorro-antd/message';
                 </tbody>
               </nz-table>
 
-              <div class="empty-tip" *ngIf="tab.artworks.length === 0">当日无上新作品</div>
+              <div class="empty-tip" *ngIf="tab.artworks.length === 0">
+                当日无上新作品
+              </div>
             </nz-tab>
           </nz-tabset>
         </nz-card>
@@ -331,7 +382,11 @@ export class DashboardComponent implements OnInit {
     const minVal = this.dauMin;
     const maxVal = this.dauMax;
     const span = Math.max(1, maxVal - minVal);
-    return this.chartHeight - this.chartPadding - ((value - minVal) / span) * (this.chartHeight - this.chartPadding * 2);
+    return (
+      this.chartHeight -
+      this.chartPadding -
+      ((value - minVal) / span) * (this.chartHeight - this.chartPadding * 2)
+    );
   }
 
   onChartMouseMove(event: MouseEvent): void {
@@ -342,7 +397,9 @@ export class DashboardComponent implements OnInit {
 
     let index = 0;
     if (this.dauTrendData.length > 1) {
-      const stepX = (this.chartWidth - this.chartPadding * 2) / (this.dauTrendData.length - 1);
+      const stepX =
+        (this.chartWidth - this.chartPadding * 2) /
+        (this.dauTrendData.length - 1);
       index = Math.round((localX - this.chartPadding) / stepX);
       index = Math.max(0, Math.min(this.dauTrendData.length - 1, index));
       this.hoveredDauX = this.chartPadding + index * stepX;
@@ -369,16 +426,15 @@ export class DashboardComponent implements OnInit {
     window.open(imageUrl, '_blank', 'noopener,noreferrer');
   }
 
-  private applyDauData(dau: { today: number; trend: Array<{ date: string; dau: number }> }): void {
+  private applyDauData(dau: {
+    today: number;
+    trend: Array<{ date: string; dau: number }>;
+  }): void {
     this.activeUsersToday = dau.today;
     this.dauTrendLabels = dau.trend.map((item) => item.date);
     this.dauTrendData = dau.trend.map((item) => item.dau);
-    this.dauMin = this.dauTrendData.length
-      ? Math.min(...this.dauTrendData)
-      : 0;
-    this.dauMax = this.dauTrendData.length
-      ? Math.max(...this.dauTrendData)
-      : 0;
+    this.dauMin = this.dauTrendData.length ? Math.min(...this.dauTrendData) : 0;
+    this.dauMax = this.dauTrendData.length ? Math.max(...this.dauTrendData) : 0;
     this.dauTrendStartDate = this.dauTrendLabels[0] || '';
     this.dauTrendEndDate =
       this.dauTrendLabels[this.dauTrendLabels.length - 1] || '';

Some files were not shown because too many files changed in this diff