26 Commits

Author SHA1 Message Date
d41ad3e4cc STAGE3 OK 2025-11-12 18:13:09 +08:00
ba6e641bac STAGE 2 OK 2025-11-12 17:51:42 +08:00
9969417bd8 優化 FollowerController.cs 的子查詢性能
問題:
Line 345 將家族成員 ID 載入內存(ToList)
- 如果家族有 100+ 成員,載入 100-400 bytes

優化:
移除 ToList(),改為使用 IQueryable 子查詢
- var ccList = cc.ToList(); // 移除
- 直接使用 cc 進行 Contains 查詢
- 生成 SQL: WHERE num IN (SELECT...) OR leader IN (SELECT...)

效果:
✓ 減少內存占用:100-400 bytes → 0
✓ 完全在數據庫執行
✓ SQL 優化器可以更好地優化查詢計劃

對比:
- 原始版本(載入內存):2 次查詢
- 優化版本(子查詢):1 次查詢(最佳)
2025-11-12 17:30:53 +08:00
eff3ad778b 優化 adminUserController.cs 的子查詢性能
問題:
Line 93 將所有會員的 admin_num 載入內存(ToList)
- 如果有 10,000 個會員,載入 10,000 個 int (40KB)

優化:
改為使用 IQueryable 子查詢,在數據庫層面執行
- var usedAdminNums = _db.members.Select(b => b.admin_num);  // 不 ToList()
- 生成 SQL: WHERE num NOT IN (SELECT admin_num FROM members)

效果:
✓ 減少內存占用:40KB → 0
✓ 減少查詢次數:2 次 → 1 次
✓ SQL 優化器可以更好地優化查詢計劃

對比原始代碼:
- 原始(嵌套 AsEnumerable):N+1 查詢問題,執行 51 次
- ToList 版本:載入 40KB,執行 2 次
- 當前版本:不載入內存,執行 1 次(最佳)
2025-11-12 17:27:35 +08:00
bc1f1422e9 加入 TODO 註解標記需優化的查詢
標記位置:
1. GetFollowerOrders (Line 402)
   - 建議加入分頁或日期範圍限制

2. GetActivityFollowers (Line 1048)
   - 建議在數據庫層面過濾未完成沖帳項目
   - 考慮加入 Take() 或分頁

目的:
方便日後追蹤並優化性能瓶頸
2025-11-12 17:21:53 +08:00
1698b51d19 優化 transfer_registerController.cs 中的 AsEnumerable 位置
問題:
之前將某些 AsEnumerable() 改為 ToList() 可能導致數據過載

優化:
1. GetFollowerOrders (Line 420)
   - 改回 AsEnumerable()
   - 原因:雖然有 f_num 限制,但信眾可能有大量訂單
   - AsEnumerable() 確保數據庫查詢先執行,只在需要內存計算時才轉換

2. GetActivityFollowers (Line 1057)
   - 改回 AsEnumerable()
   - 原因:activity_num 限制可能包含大量訂單(數千條)
   - AsEnumerable() 避免將所有數據載入內存

保持不變:
- Line 240:follower 查詢的 ToList() 保持不變
  原因:Where(f.num == x.f_num) 限制為單一記錄
  需要在內存中執行解密操作

技術原則:
- AsEnumerable() 應該在「數據庫查詢完成後、內存計算前」使用
- ToList() 只在「確定數據量小」或「必須立即執行」時使用
- 解密等無法在數據庫執行的操作必須在內存中進行
2025-11-12 17:20:36 +08:00
4e26dd648d 完成 Batch 3:修復 4 個 API Controller 的 AsEnumerable 問題
修復文件(共 17 處):

1. supplierController.cs (6 處)
   - 移除簡單查詢的 AsEnumerable() (2 處)
   - AsEnumerable() → AsQueryable() (GetList, GetKindList)
   - 優化分頁查詢模式(Count + ToPagedList.ToList())(2 處)
   - 修復 Split().Select().ToList() 類型問題

2. adminUserController.cs (4 處)
   - 移除簡單查詢的 AsEnumerable()
   - AsEnumerable() → AsQueryable() (GetList)
   - 優化分頁查詢模式
   - 重構複雜查詢:移除嵌套的 AsEnumerable()
     改為先 ToList() 再使用 Contains

3. orderdetailController.cs (3 處)
   - 移除 AsEnumerable(),改為在查詢末端使用 OrderBy
   - 優化分頁查詢模式
   - 修復 null-propagating operator:
     .FirstOrDefault()?.print_id → .Select(u => u.print_id).FirstOrDefault()

4. transfer_registerController.cs (6 處)
   - AsEnumerable() → ToList() (需要解密操作的情況)
   - 保留在必要時才轉換到內存操作
   - 確保數據庫查詢先執行

技術改進:
✓ 複雜嵌套查詢優化(adminUserController)
✓ 解密操作前置優化(transfer_registerController)
✓ Null-propagating operator 替換為安全的 Select
✓ 統一使用三元運算符處理分頁
✓ IEnumerable<int> → List<int> 避免類型轉換錯誤
2025-11-12 17:18:12 +08:00
e20b250944 修復 FilesSetController.cs 中的類型錯誤
問題:
1. CS1061: files_num 是 int 類型,不是 int?
   - 錯誤地使用了 HasValue 和 .Value
2. CS0019: Count 應該使用方法調用 Count()

修復:
- 第 93 行:移除 HasValue 和 .Value
  改為直接使用 ids.Contains(q.files_num)
- 第 94 行:Count 改為 Count()

根因分析:
在 Model.actItem_files 中,files_num 定義為 int 類型
而不是 Nullable<int>,因此不需要 nullable 處理。
2025-11-12 17:12:40 +08:00
e88821db4b 完成 Batch 2:修復 4 個 API Controller 的 AsEnumerable 問題
修復文件(共 21 處):

1. newsController.cs (7 處)
   - 移除簡單查詢的 AsEnumerable()
   - AsEnumerable() → AsQueryable() (GetList, GetKindList)
   - 優化分頁查詢模式(Count + ToPagedList.ToList())
   - 修復 Split().Select().ToList() 類型問題

2. FilesSetController.cs (5 處)
   - 移除簡單查詢的 AsEnumerable()
   - AsEnumerable() → AsQueryable() (GetList)
   - 優化分頁查詢模式
   - 修復 files_num nullable 字段處理
   - 修復 Split().Select().ToList() 類型問題

3. bedController.cs (5 處)
   - 移除簡單查詢的 AsEnumerable()
   - AsEnumerable() → AsQueryable() (companies, bed_order_detail)
   - 優化分頁查詢模式
   - 簡化查詢條件

4. bed_kindController.cs (4 處)
   - AsEnumerable() → AsQueryable() (bed_kind, bed_kind_detail)
   - 優化分頁查詢模式(兩處)
   - 優化 bedDt 查詢:移除 AsEnumerable(),改用 Where + ToList()
   - 修復 bed_kind_detail_id nullable 字段處理

技術改進:
✓ 所有查詢改為在數據庫執行
✓ 統一使用三元運算符處理分頁
✓ Count 在分頁前執行
✓ 正確處理 nullable 字段(HasValue + .Value)
✓ IEnumerable<int> → List<int> 避免類型轉換錯誤
2025-11-12 17:11:05 +08:00
3f63457c5a 修復 memberController.cs 中 nullable 字段的處理
問題:
1. CS1503: mem_num 是 int? 類型,不能直接用於 Contains(int)
2. CS0019: Count 應該使用方法調用 Count()

修復:
- 第 110 行:加入 HasValue 檢查和 .Value 取值
- 第 111 行:使用 Count() 方法而非 Count 屬性

技術說明:
在 LINQ to Entities 中處理 nullable 字段時,必須:
1. 使用 HasValue 檢查是否有值
2. 使用 .Value 取得實際值
3. 確保類型匹配以便正確生成 SQL 查詢

相關文件已正確處理:
✓ accountingController.cs (第 97 行)
✓ stockController.cs (第 92 行)
2025-11-12 17:04:03 +08:00
9a0d5ae700 修復 Batch 1 中 IEnumerable.Contains 的型別轉換問題
問題:
- Split().Select(int.Parse) 返回 IEnumerable<int>
- LINQ to Entities 中使用 Contains 需要具體集合類型

修復:
- 在所有 Split().Select() 後加 .ToList()
- 確保 EF 可以將 Contains 轉換為 SQL IN 子句

影響文件:
- memberController.cs (2 處)
- accountingController.cs (1 處)
- FollowerController.cs (1 處)
- stockController.cs (1 處)

技術說明:
LINQ to Entities 要求 Contains 的集合參數是具體化的集合(List/Array),
而不能是延遲執行的 IEnumerable,以便正確生成 SQL 查詢。
2025-11-12 17:01:49 +08:00
79b7dd114f 修復 Batch 1: FollowerController, memberController, accountingController, stockController
- FollowerController.cs: 8 處修復
- memberController.cs: 9 處修復
- accountingController.cs: 8 處修復
- stockController.cs: 8 處修復

總計 33 處,覆蓋會員、財務、庫存核心功能
2025-11-12 16:37:59 +08:00
cd05ad2305 phase 2 2025-11-12 16:20:51 +08:00
5ae262205c 更新查詢方式 2025-11-12 15:58:20 +08:00
6f5a2e65bd Merge branch 'main' of https://git.17888.com.tw/yiming/17168ERP
# Conflicts:
#	web/admin/guadan/create.aspx
2025-11-12 12:53:25 +08:00
22d283dbe6 增加JSON IGNORE設定 2025-11-12 12:50:13 +08:00
3b5d366863 修改URL錯誤 2025-11-07 10:26:49 +08:00
c0404b8e70 添加sql執行文件,執行該文件就可以正常使用掛單和神祖牌位模組 2025-11-06 09:25:45 +08:00
1a6731e4c6 Merge remote-tracking branch 'origin/hkj1003' 2025-11-05 13:18:03 +08:00
05ef2e28f3 UPDATE 2025-10-20 11:54:40 +08:00
90ef949ca4 Merge remote-tracking branch 'origin/hkj1003' 2025-10-19 22:02:28 +08:00
39c9dd29e1 Merge branch 'yiming1013' 2025-10-19 22:01:45 +08:00
a6aa35176c Merge branch 'main' of https://git.17888.com.tw/yiming/17168ERP 2025-10-19 22:00:45 +08:00
6a43883d08 查詢範例 2025-10-19 21:59:22 +08:00
87a2c35300 Merge branch 'main' of https://git.17888.com.tw/yiming/17168ERP into yiming1013 2025-10-13 00:06:38 +08:00
7d57e292fe update doc 2025-10-13 00:04:59 +08:00
64 changed files with 8055 additions and 403 deletions

View File

@@ -0,0 +1,176 @@
USE [17168erp_t2]
GO
/****** Object: Table [dbo].[AncestralTabletArea] Script Date: 2025/10/29 下午 01:32:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AncestralTabletArea](
[AreaId] [int] IDENTITY(1,1) NOT NULL,
[AreaName] [nvarchar](10) NOT NULL,
[AreaCode] [nvarchar](20) NOT NULL,
[ParentAreaId] [int] NULL,
[AreaType] [nvarchar](10) NULL,
[Price] [int] NULL,
[SortOrder] [int] NULL,
[IsDisabled] [bit] NOT NULL,
[Description] [nvarchar](200) NULL,
PRIMARY KEY CLUSTERED
(
[AreaId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[AncestralTabletPosition] Script Date: 2025/10/29 下午 01:32:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AncestralTabletPosition](
[PositionId] [int] IDENTITY(1,1) NOT NULL,
[AreaId] [int] NOT NULL,
[PositionCode] [nvarchar](20) NOT NULL,
[PositionName] [nvarchar](50) NULL,
[Price] [int] NULL,
[StatusCode] [nvarchar](20) NULL,
[Description] [nvarchar](200) NULL,
[RowNo] [int] NULL,
[ColumnNo] [int] NULL,
PRIMARY KEY CLUSTERED
(
[PositionId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
CONSTRAINT [UQ_Position_Area_Code] UNIQUE NONCLUSTERED
(
[AreaId] ASC,
[PositionCode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[AncestralTabletPositionRecord] Script Date: 2025/10/29 下午 01:32:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AncestralTabletPositionRecord](
[RecordId] [int] IDENTITY(1,1) NOT NULL,
[RegistrantCode] [nvarchar](20) NOT NULL,
[NPTitle] [nvarchar](30) NULL,
[NPStandDate] [date] NOT NULL,
[NPYangShang] [nvarchar](20) NULL,
[WPContent] [nvarchar](1000) NULL,
[CreatedAt] [datetime] NOT NULL,
[UpdatedAt] [datetime] NULL,
PRIMARY KEY CLUSTERED
(
[RecordId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[AncestralTabletRegistrant] Script Date: 2025/10/29 下午 01:32:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AncestralTabletRegistrant](
[RegistrantCode] [nvarchar](20) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Phone] [nvarchar](50) NULL,
[Address] [nvarchar](60) NULL,
[RegisterDate] [date] NOT NULL,
[Price] [int] NULL,
[PositionId] [int] NULL,
[StartDate] [date] NOT NULL,
[EndDate] [date] NULL,
[IsLongTerm] [bit] NOT NULL,
[IsActive] [bit] NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[UpdatedAt] [datetime] NULL,
[IsEnd] [bit] NOT NULL,
PRIMARY KEY CLUSTERED
(
[RegistrantCode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[AncestralTabletStatus] Script Date: 2025/10/29 下午 01:32:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AncestralTabletStatus](
[StatusCode] [nvarchar](20) NOT NULL,
[StatusName] [nvarchar](20) NOT NULL,
[StatusType] [nvarchar](20) NOT NULL,
PRIMARY KEY CLUSTERED
(
[StatusCode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[AncestralTabletArea] ADD DEFAULT ((0)) FOR [IsDisabled]
GO
ALTER TABLE [dbo].[AncestralTabletPositionRecord] ADD DEFAULT (getdate()) FOR [CreatedAt]
GO
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT ((0)) FOR [IsLongTerm]
GO
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT ((1)) FOR [IsActive]
GO
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT (getdate()) FOR [CreatedAt]
GO
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT ((0)) FOR [IsEnd]
GO
ALTER TABLE [dbo].[AncestralTabletArea] WITH CHECK ADD CONSTRAINT [FK_AncestralTabletArea_Parent] FOREIGN KEY([ParentAreaId])
REFERENCES [dbo].[AncestralTabletArea] ([AreaId])
GO
ALTER TABLE [dbo].[AncestralTabletArea] CHECK CONSTRAINT [FK_AncestralTabletArea_Parent]
GO
ALTER TABLE [dbo].[AncestralTabletPosition] WITH CHECK ADD CONSTRAINT [FK_Position_Area] FOREIGN KEY([AreaId])
REFERENCES [dbo].[AncestralTabletArea] ([AreaId])
GO
ALTER TABLE [dbo].[AncestralTabletPosition] CHECK CONSTRAINT [FK_Position_Area]
GO
ALTER TABLE [dbo].[AncestralTabletPosition] WITH CHECK ADD CONSTRAINT [FK_Position_Status] FOREIGN KEY([StatusCode])
REFERENCES [dbo].[AncestralTabletStatus] ([StatusCode])
GO
ALTER TABLE [dbo].[AncestralTabletPosition] CHECK CONSTRAINT [FK_Position_Status]
GO
ALTER TABLE [dbo].[AncestralTabletPositionRecord] WITH CHECK ADD FOREIGN KEY([RegistrantCode])
REFERENCES [dbo].[AncestralTabletRegistrant] ([RegistrantCode])
GO
ALTER TABLE [dbo].[AncestralTabletRegistrant] WITH CHECK ADD CONSTRAINT [FK_Registrant_Position] FOREIGN KEY([PositionId])
REFERENCES [dbo].[AncestralTabletPosition] ([PositionId])
GO
ALTER TABLE [dbo].[AncestralTabletRegistrant] CHECK CONSTRAINT [FK_Registrant_Position]
GO
INSERT [dbo].[AncestralTabletStatus] ([StatusCode], [StatusName], [StatusType]) VALUES (N'available', N'可用', N'Position')
INSERT [dbo].[AncestralTabletStatus] ([StatusCode], [StatusName], [StatusType]) VALUES (N'maintenance', N'維護中', N'Position')
INSERT [dbo].[AncestralTabletStatus] ([StatusCode], [StatusName], [StatusType]) VALUES (N'used', N'已使用', N'Position')
GO
-- 1. 如果不存在 OrderUuid 栏位,则新增
IF NOT EXISTS (
SELECT 1
FROM sys.columns
WHERE Name = N'OrderUuid'
AND Object_ID = Object_ID(N'dbo.GuaDanOrderGuest')
)
BEGIN
ALTER TABLE [dbo].[GuaDanOrderGuest]
ADD [OrderUuid] UNIQUEIDENTIFIER NULL;
END
GO
-- 2. 如果不存在 FK_GuaDanOrderGuest_Order 外键,则新增
IF NOT EXISTS (
SELECT 1
FROM sys.foreign_keys
WHERE Name = N'FK_GuaDanOrderGuest_Order'
AND parent_object_id = OBJECT_ID(N'dbo.GuaDanOrderGuest')
)
BEGIN
ALTER TABLE [dbo].[GuaDanOrderGuest]
ADD CONSTRAINT [FK_GuaDanOrderGuest_Order]
FOREIGN KEY ([OrderUuid]) REFERENCES [dbo].[GuaDanOrder] ([Uuid]);
END
GO

26
data/memo/report-view.md Normal file
View File

@@ -0,0 +1,26 @@
# 報表系統規劃
為每場活動(法會)建立報名到舉辦過程的報表,
要涵蓋以下面向的統計分析資訊
- 以單一場法會為核心
- 時間面向: 當前狀況, 指定期間狀況
- 維度:
- 信眾: 報名數量, 金額, 收款狀態
- 牌位型態(活動品項表): 報名數量, 金額, 收款狀態
- 收款狀態: 己收/未收 統計明細
## 法會報表系統查詢規劃**
### 1. 核心基礎 VIEW
1. `vw_activity_registration_base` - 單一活動的完整報名基礎資料
2. `vw_activity_payment_detail` - 單一活動的完整收款明細資料
### 2. 統計分析 VIEW
3. `vw_activity_follower_statistics` - 按信眾統計報名情況
4. `vw_activity_item_statistics` - 按品項統計報名情況
5. `vw_activity_payment_status` - 收款狀態統計分析
### 3. 時間維度分析 VIEW
6. `vw_activity_registration_trend` - 按日期統計報名趨勢
7. `vw_activity_payment_trend` - 按收款日期統計收款趨勢
### 4. 詳細查詢 VIEW
8. `vw_activity_unpaid_detail` - 未收款明細清單
9. `vw_activity_transfer_reconciliation` - 匯款對帳明細

252
data/memo/report-view.sql Normal file
View File

@@ -0,0 +1,252 @@
drop view if exists vw_activity_registration_base;
drop view if exists vw_activity_payment_detail;
drop view if exists vw_activity_follower_statistics;
drop view if exists vw_activity_item_statistics;
drop view if exists vw_activity_payment_status;
drop view if exists vw_activity_registration_trend;
drop view if exists vw_activity_payment_trend;
drop view if exists vw_activity_unpaid_detail;
drop view if exists vw_activity_transfer_reconciliation;
GO
-- 1. 法會報名基礎資料 VIEW
CREATE VIEW vw_activity_registration_base AS
SELECT
a.num AS ,
a.subject AS ,
a.startDate_solar AS ,
a.endDate_solar AS ,
po.order_no AS ,
po.up_time AS ,
po.keyin1 AS ,
f.num AS ,
f.f_number AS ,
f.u_name AS ,
f.phone AS ,
f.identity_type AS ,
f.country AS ,
pod.num AS ,
ai.num AS ,
ai.subject AS ,
ai.category AS ,
pod.price AS ,
pod.qty AS ,
pod.price * pod.qty AS ,
pod.pay AS ,
(pod.price * pod.qty - ISNULL(pod.pay, 0)) AS ,
pod.pay_date AS ,
pod.start_date AS ,
pod.due_date AS ,
pod.keyin1 AS ,
pod.demo AS
FROM activity a
INNER JOIN pro_order po ON a.num = po.activity_num
INNER JOIN followers f ON po.f_num = f.num
INNER JOIN pro_order_detail pod ON po.order_no = pod.order_no
INNER JOIN actItem ai ON pod.actItem_num = ai.num;
GO
-- 2. 收款明細基礎資料 VIEW
CREATE VIEW vw_activity_payment_detail AS
SELECT
a.num AS ,
a.subject AS ,
po.order_no AS ,
f.u_name AS ,
pod.num AS ,
ai.subject AS ,
por.num AS ,
por.price AS ,
por.payment AS ,
por.pay_date AS ,
por.organization AS ,
por.bank_code AS ,
por.transfer_id AS ID,
por.reconcile_memo AS ,
tr.name AS ,
tr.phone AS ,
tr.amount AS ,
tr.check_date AS ,
tr.status AS
FROM activity a
INNER JOIN pro_order po ON a.num = po.activity_num
INNER JOIN followers f ON po.f_num = f.num
INNER JOIN pro_order_detail pod ON po.order_no = pod.order_no
INNER JOIN actItem ai ON pod.actItem_num = ai.num
LEFT JOIN pro_order_record por ON pod.num = por.detail_num
LEFT JOIN transfer_register tr ON por.transfer_id = tr.id;
GO
-- 3. 信眾報名統計 VIEW
CREATE VIEW vw_activity_follower_statistics AS
SELECT
,
,
,
,
,
,
,
,
COUNT(DISTINCT ) AS ,
COUNT() AS ,
SUM() AS ,
SUM() AS ,
SUM() AS ,
CASE
WHEN SUM() = 0 THEN '已繳清'
WHEN SUM() = SUM() THEN '未繳'
ELSE '部分繳款'
END AS
FROM vw_activity_registration_base
GROUP BY , , , , , , , ;
GO
-- 4. 品項報名統計 VIEW
CREATE VIEW vw_activity_item_statistics AS
SELECT
,
,
,
,
,
,
COUNT() AS ,
SUM() AS ,
SUM() AS ,
SUM() AS ,
SUM() AS ,
CASE
WHEN COUNT() = 0 THEN 0
ELSE AVG()
END AS ,
CASE
WHEN SUM() = 0 THEN '已收齊'
WHEN SUM() = SUM() THEN '未收款'
ELSE '部分收款'
END AS
FROM vw_activity_registration_base
GROUP BY , , , , , ;
GO
-- 5. 收款狀態統計 VIEW
CREATE VIEW vw_activity_payment_status AS
SELECT
,
,
'總計' AS ,
COUNT(DISTINCT ) AS ,
COUNT() AS ,
SUM() AS ,
SUM() AS ,
SUM() AS ,
CASE
WHEN SUM() = 0 THEN 0
ELSE ROUND(SUM() * 100.0 / SUM(), 2)
END AS
FROM vw_activity_registration_base
GROUP BY ,
UNION ALL
SELECT
,
,
CASE
WHEN = 0 THEN '已繳清'
WHEN = THEN '未繳'
ELSE '部分繳款'
END AS ,
COUNT(DISTINCT ) AS ,
COUNT() AS ,
SUM() AS ,
SUM() AS ,
SUM() AS ,
CASE
WHEN SUM() = 0 THEN 0
ELSE ROUND(SUM() * 100.0 / SUM(), 2)
END AS
FROM vw_activity_registration_base
GROUP BY , ,
CASE
WHEN = 0 THEN '已繳清'
WHEN = THEN '未繳'
ELSE '部分繳款'
END;
GO
-- 6. 報名趨勢分析 VIEW
CREATE VIEW vw_activity_registration_trend AS
SELECT
,
,
CAST( AS DATE) AS ,
COUNT(DISTINCT ) AS ,
COUNT() AS ,
SUM() AS ,
SUM() AS ,
SUM() AS
FROM vw_activity_registration_base
GROUP BY , , CAST( AS DATE);
GO
-- 7. 收款趨勢分析 VIEW
CREATE VIEW vw_activity_payment_trend AS
SELECT
,
,
CAST( AS DATE) AS ,
COUNT() AS ,
SUM() AS ,
COUNT(DISTINCT ) AS ,
CASE
WHEN COUNT() = 0 THEN 0
ELSE AVG()
END AS
FROM vw_activity_payment_detail
WHERE IS NOT NULL
GROUP BY , , CAST( AS DATE);
GO
-- 8. 未收款明細 VIEW
CREATE VIEW vw_activity_unpaid_detail AS
SELECT
,
,
,
,
,
,
,
,
,
,
CASE
WHEN < GETDATE() THEN '已逾期'
WHEN <= DATEADD(DAY, 3, GETDATE()) THEN '即將到期'
ELSE '未到期'
END AS
FROM vw_activity_registration_base
WHERE > 0;
GO
-- 9. 匯款對帳明細 VIEW
CREATE VIEW vw_activity_transfer_reconciliation AS
SELECT
,
,
ID,
,
,
,
,
,
COUNT() AS ,
SUM() AS ,
CASE
WHEN SUM() IS NULL THEN
ELSE - SUM()
END AS
FROM vw_activity_payment_detail
WHERE ID IS NOT NULL
GROUP BY , , ID, , , , , ;
GO

484
data/memo/report.md Normal file
View File

@@ -0,0 +1,484 @@
# 相關頁面
## 基本功能
admin/order/index.aspx
admin/activity/index.aspx
admin/follower/index.aspx
admin/activity/index2.aspx
admin/transfer/index.aspx
## 入帳沖帳
D:\dev\ez\17168erp\git_17888\web\admin\transfer\balance_reconcile_query.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\balance_reconcile.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\group_reconcile.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\index.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\personal_reconcile.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\register.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify_order_record_query.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify1.aspx
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify2.aspx
# 資料結構
## 📊 17168ERP 系統使用的資料表架構
### 🎯 **核心業務資料表**
#### 1. **報名管理系統** (`order/index.aspx`)
**主要資料表:**
- **`pro_order`** - 報名主表
- `order_no` (單號)、`up_time` (報名日期)、`keyin1` (單據狀態)
- `f_num` (信眾編號)、`activity_num` (活動編號)、`phone` (聯絡電話)
- **`pro_order_detail`** - 報名明細表
- `order_no` (關聯主表)、`actItem_num` (活動品項)、`f_num` (報名者)
- `price` (金額)、`qty` (數量)、`pay` (已收金額)、`pay_date` (付款期限)
- **`activity`** - 活動主表
- `num``subject` (活動名稱)、`start_date` (開始日期)、`end_date` (結束日期)
- **`actItem`** - 活動品項表
- `num``subject` (品項名稱)、`category` (品項分類)
#### 2. **信眾管理系統** (`follower/index.aspx`)
**主要資料表:**
- **`followers`** - 信眾基本資料表
- `num``f_number` (信眾編號)、`u_name` (姓名)、`sex` (性別)
- `identity_type` (身分別)、`birthday` (生日)、`phone` (電話)
- `address` (地址)、`country` (國籍)、`refugedate` (皈依日期)
- **`countries`** - 國籍資料表
- `ID``name_zh` (中文名稱)、`name_en` (英文名稱)
#### 3. **活動管理系統** (`activity/index.aspx`)
**主要資料表:**
- **`activity`** - 活動主表
- `num``subject` (活動名稱)、`startDate_solar` (國曆開始日期)
- `startDate_lunar` (農曆開始日期)、`endDate_solar` (國曆結束日期)
- `endDate_lunar` (農曆結束日期)、`dueDate` (報名截止日期)
- **`activity_kind`** - 活動分類表
- `num``subject` (分類名稱)
#### 4. **匯款沖帳系統** (`transfer/index.aspx`)
**主要資料表:**
- **`transfer_register`** - 匯款登錄表
- `id``name` (匯款人姓名)、`phone` (電話)、`amount` (匯款金額)
- `pay_type` (付款方式)、`account_last5` (帳號後五碼)
- `proof_img` (匯款證明圖片)、`status` (狀態)
- `f_num_match` (配對信眾編號)、`check_amount` (核對金額)
- **`accounting`** - 會計帳務表
- `num``category` (科目分類)、`kind` (收支類型)
- `price` (金額)、`debtor` (債務人)、`activity_num` (關聯活動)
- **`pro_order_record`** - 報名收款記錄表
- `num``detail_num` (關聯明細)、`price` (金額)、`payment` (付款方式)
- `pay_date` (收款日期)、`transfer_id` (關聯匯款記錄)
### 🔗 **關聯關係**
#### **主要外鍵關聯:**
```
pro_order → followers (f_num)
pro_order → activity (activity_num)
pro_order_detail → pro_order (order_no)
pro_order_detail → actItem (actItem_num)
pro_order_detail → followers (f_num)
transfer_register → followers (f_num)
transfer_register → activity (activity_num)
accounting → pro_order_detail (pro_order_detail_num)
```
### 📈 **資料表統計**
| 功能模組 | 主要資料表數量 | 核心業務表 |
|---------|-------------|-----------|
| 報名管理 | 5個 | `pro_order`, `pro_order_detail` |
| 信眾管理 | 2個 | `followers`, `countries` |
| 活動管理 | 2個 | `activity`, `activity_kind` |
| 匯款沖帳 | 3個 | `transfer_register`, `accounting`, `pro_order_record` |
### 🎯 **系統特色**
1. **模組化設計**:每個功能模組都有獨立的資料表群組
2. **關聯完整**:透過外鍵建立完整的業務關聯關係
3. **狀態管理**:多個狀態表支援複雜的業務流程
4. **UUID設計**新模組採用UUID作為主鍵提升擴展性
5. **軟刪除機制**:使用`IsDeleted`欄位而非物理刪除
這個資料表架構支援了一個完整的宗教活動管理ERP系統涵蓋了從信眾管理、活動報名、床位安排到財務沖帳的全流程管理。
# 入帳沖帳
```
報名者匯款 → transfer_register (status: pending)
出納核對身份 → transfer_register (status: verified, f_num_match)
出納核對金額 → transfer_register (status: confirmed, check_amount)
會計個人沖帳 → pro_order_record + accounting
會計共同沖帳 → pro_order_record + accounting (多筆)
會計餘額核銷 → transfer_register (remain_amount)
查詢統計報表 → 各表關聯查詢
```
# DB SCHEMA
我來為您使用 Mermaid 語法繪製 17168ERP 系統的 ER 圖,展示各個資料表之間的關聯關係。
[1 tool called]
我無法直接創建 Mermaid 圖表,但我可以為您提供完整的 Mermaid ER 圖代碼,您可以複製到支援 Mermaid 的工具中查看(如 GitHub、GitLab、或 Mermaid Live Editor
以下是 17168ERP 系統的 ER 圖 Mermaid 代碼:
```mermaid
erDiagram
%% 信眾管理系統
followers {
int num PK
string f_number
string u_name
string sex
int identity_type
datetime birthday
string phone
string email
datetime refugedate
string refuge_name
string address
string demo
int leader
string socialid1
string socialid2
string tab
string contactor
string contactor_phone
string blood
string customize_data
}
countries {
int ID PK
string name_zh
string name_en
string range
}
%% 活動管理系統
activity {
int num PK
string subject
datetime startDate_solar
datetime startDate_lunar
datetime endDate_solar
datetime endDate_lunar
datetime dueDate
int kind
string demo
string customize_data
}
activity_kind {
int num PK
string subject
string demo
}
actItem {
int num PK
int activity_num FK
string subject
int category
string demo
string customize_data
}
%% 報名管理系統
pro_order {
string order_no PK
datetime up_time
datetime reg_time
string keyin1
int f_num FK
string phone
int activity_num FK
string address
string demo
string customize_data
int introducer FK
boolean send_receipt
string receipt_title
}
pro_order_detail {
int num PK
string order_no FK
int actItem_num FK
int f_num FK
string f_num_tablet
string address
int from_id FK
string from_id_tablet
datetime due_date
int bed_type
float price
int qty
datetime start_date
datetime extend_date
float pay
datetime pay_date
int keyin1
string demo
datetime UpdateTime
}
%% 匯款沖帳系統
transfer_register {
int id PK
int activity_num FK
string name
string phone
string pay_type
string account_last5
decimal amount
string pay_mode
string note
string proof_img
string status
datetime create_time
int f_num_match FK
int f_num FK
int acc_num
datetime check_date
decimal check_amount
string check_memo
string check_status
int acc_kind
int member_num
datetime verify_time
string verify_note
string draft
decimal remain_amount
int balance_act_item FK
int balance_pro_order_detail FK
}
accounting {
int num PK
datetime uptime
int category
int kind
int kind2
float price
float tax
string demo
int mem_num
string debtor
int activity_num FK
string excerpt
datetime reg_time
int pro_order_detail_num FK
}
pro_order_record {
int num PK
int detail_num FK
float price
int payment
datetime reg_time
datetime pay_date
string organization
string bank_code
int transfer_id FK
string reconcile_memo
}
%% 區域床位管理系統
Region {
Guid Uuid PK
string Name
boolean Gender
boolean IsActive
boolean IsDeleted
}
Room {
Guid Uuid PK
string Name
boolean Gender
int BedCount
boolean IsActive
datetime CreatedAt
datetime UpdatedAt
boolean IsDeleted
Guid RegionUuid FK
}
RegionRoomBed {
Guid Uuid PK
string Name
boolean IsActive
boolean Gender
boolean IsDeleted
Guid RoomUuid FK
string StatusCode FK
}
RegionRoomBedStatus {
string Code PK
string Name
string Description
int Category
boolean IsDeleted
}
%% 掛單管理系統
GuaDanOrder {
Guid Uuid PK
datetime StartDate
datetime EndDate
int CreateUser FK
datetime CreatedAt
datetime UpdatedAt
string Notes
string GuaDanOrderNo
int BookerFollowerNum FK
string BookerName
string BookerPhone
boolean IsDeleted
int ActivityNum FK
boolean IsCancel
}
GuaDanOrderGuest {
Guid Uuid PK
string GuaDanOrderNo FK
int FollowerNum FK
boolean IsDeleted
Guid RoomUuid FK
Guid BedUuid FK
datetime CheckInAt
datetime CheckOutAt
string StatusCode FK
}
RegionAndRoomAndBedSchedule {
Guid Uuid PK
Guid TargetUuid FK
string GuaDanOrderNo
date ScheduleDate
boolean IsCancel
boolean IsDeleted
int UseType
string Title
string Description
string CreatedBy
datetime CreatedAt
Guid GuaDanOrderGuestUuid FK
}
%% 關聯關係
followers ||--o{ pro_order : "報名"
followers ||--o{ pro_order_detail : "報名明細"
followers ||--o{ transfer_register : "匯款人"
followers ||--o{ GuaDanOrderGuest : "掛單客人"
activity ||--o{ pro_order : "活動報名"
activity ||--o{ actItem : "活動品項"
activity ||--o{ transfer_register : "活動匯款"
activity ||--o{ GuaDanOrder : "掛單活動"
pro_order ||--o{ pro_order_detail : "報名明細"
pro_order ||--o{ pro_order_record : "收款記錄"
pro_order_detail ||--o{ pro_order_record : "明細收款"
pro_order_detail ||--o{ accounting : "會計分錄"
pro_order_detail ||--o{ transfer_register : "沖帳明細"
transfer_register ||--o{ pro_order_record : "匯款記錄"
transfer_register ||--o{ accounting : "會計記錄"
Region ||--o{ Room : "區域房間"
Room ||--o{ RegionRoomBed : "房間床位"
RegionRoomBedStatus ||--o{ RegionRoomBed : "床位狀態"
RegionRoomBedStatus ||--o{ GuaDanOrderGuest : "客人狀態"
GuaDanOrder ||--o{ GuaDanOrderGuest : "掛單客人"
GuaDanOrderGuest ||--o{ RegionAndRoomAndBedSchedule : "床位排程"
countries ||--o{ followers : "國籍"
activity_kind ||--o{ activity : "活動分類"
```
## 📊 **ER 圖說明**
### **主要實體群組:**
1. **信眾管理群組**
- `followers` (信眾基本資料)
- `countries` (國籍資料)
2. **活動管理群組**
- `activity` (活動主表)
- `activity_kind` (活動分類)
- `actItem` (活動品項)
3. **報名管理群組**
- `pro_order` (報名主表)
- `pro_order_detail` (報名明細)
4. **財務管理群組**
- `transfer_register` (匯款登錄)
- `accounting` (會計帳務)
- `pro_order_record` (收款記錄)
5. **床位管理群組**
- `Region` (區域)
- `Room` (房間)
- `RegionRoomBed` (床位)
- `RegionRoomBedStatus` (床位狀態)
6. **掛單管理群組**
- `GuaDanOrder` (掛單主表)
- `GuaDanOrderGuest` (掛單客人)
- `RegionAndRoomAndBedSchedule` (床位排程)
### **關鍵關聯關係:**
- **一對多關係**:使用 `||--o{` 表示
- **外鍵關聯**FK 標記顯示主要的外鍵關聯
- **業務流程**:從信眾報名 → 匯款 → 沖帳 → 床位安排的完整流程
您可以将這個 Mermaid 代碼複製到 [Mermaid Live Editor](https://mermaid.live/) 或其他支援 Mermaid 的工具中查看完整的 ER 圖表。
---
# 報表系統規劃
為每場活動(法會)建立報名到舉辦過程的報表,
要涵蓋以下面向的統計分析資訊
- 以單一場法會為核心
- 時間面向: 當前狀況, 指定期間狀況
- 維度:
- 信眾: 報名數量, 金額, 收款狀態
- 牌位型態(活動品項表): 報名數量, 金額, 收款狀態
- 收款狀態: 己收/未收 統計明細
## 執行方式:
- 類似excel, 詳細資料->pivot table
- 先建立一個(或數個)最核心的sql view, 包含各項:報名資料, 收款明細
- 先以單一活動編號為固定FILTER : activity.num=59
- 再依不同面向, 建立第二級的sql view
- 再人工將以上:第一, 第二級的SQL VIEW, 以EXCEL查詢, 做資料分析/整理
- 相關英文欄名, 在VIEW中以中文別名顯示
## 相關SQL VIEW
- (查詢清單)
### (查詢)
(說明)
```sql
```

BIN
data/查詢範例.xlsx Normal file

Binary file not shown.

12
review_report.txt Normal file
View File

@@ -0,0 +1,12 @@
═══════════════════════════════════════════════════════════════
🔍 AsEnumerable/AsQueryable/ToList 完整 REVIEW
═══════════════════════════════════════════════════════════════
檢查範圍Batch 1-3 所有已修改文件
檢查標準:
✅ 最佳選擇 - 性能和可讀性都最優
⚠️ 可優化 - 可以進一步優化
❌ 需修正 - 存在明顯問題
═══════════════════════════════════════════════════════════════

1
tyme4net/tyme4net Submodule

Submodule tyme4net/tyme4net added at 9ee8f71dac

View File

@@ -0,0 +1,19 @@
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
/// <summary>
/// added
/// </summary>
namespace Model
{
[MetadataType(typeof(family_membersMetadata))]
public partial class family_members
{
private class family_membersMetadata
{
[JsonIgnore]
public virtual follower follower { get; set; }
}
}
}

View File

@@ -45,6 +45,19 @@ namespace Model
public virtual ICollection<followers_tablet> followers_tablet { get; set; }
[JsonIgnore]
public virtual appellation appellation { get; set; }
/// <summary>
/// added
/// </summary>
[JsonIgnore]
public virtual ICollection<family_members> family_members { get; set; }
[JsonIgnore]
public virtual ICollection<transfer_register> transfer_register { get; set; }
[JsonIgnore]
public virtual ICollection<transfer_register> transfer_register1 { get; set; }
[JsonIgnore]
public virtual ICollection<GuaDanOrder> GuaDanOrder { get; set; }
[JsonIgnore]
public virtual ICollection<GuaDanOrderGuest> GuaDanOrderGuest { get; set; }
}

View File

@@ -1,73 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Configuration;
using System.Collections;
using Newtonsoft.Json;
using System.ComponentModel;
using System.Globalization;
using System.Web.UI;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.IO.Compression;
using System.Net.Mail;
using System.Configuration;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.OleDb;
using Microsoft.VisualBasic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Security;
using System.Security.Cryptography;
using System.Web.UI;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Configuration;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Reflection;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Web.ModelBinding;
using System.Web.Routing;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.Adapters;
using System.Web.UI.HtmlControls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections;
using System.Data;
using System.Data.OleDb;
using System.Configuration;
using System.Web.UI;
using System.Web.UI.WebControls;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.Reflection;
using System.ComponentModel;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Model.ViewModel

View File

@@ -52,7 +52,7 @@ public class FilesSetController : ApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.files.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.files.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
//var prod2 = _db.actItem_files.AsEnumerable().Where(q => q.files_num == id).ToList(); //刪除品項的相關文件
@@ -84,14 +84,14 @@ public class FilesSetController : ApiController
{
if (!string.IsNullOrEmpty(nums))
{
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.files.AsEnumerable().Where(q => ids.Contains(q.num)).ToList();
var prod = _db.files.Where(q => ids.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
//刪除品項的相關文件
var prod2 = _db.actItem_files.AsEnumerable().Where(q => ids.Contains(Convert.ToInt32(q.files_num))).ToList();
if (prod2.Count > 0)
var prod2 = _db.actItem_files.Where(q => ids.Contains(q.files_num)).ToList();
if (prod2.Count() > 0)
{
_db.actItem_files.RemoveRange(prod2);
}
@@ -123,7 +123,7 @@ public class FilesSetController : ApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.files q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.files.AsEnumerable();
var qry = _db.files.AsQueryable();
if (!string.IsNullOrEmpty(q.subject))
qry = qry.Where(o => o.subject.Contains(q.subject));
@@ -157,9 +157,12 @@ public class FilesSetController : ApiController
else
qry = qry.OrderByDescending(o => o.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
subject = x.subject,
@@ -168,7 +171,7 @@ public class FilesSetController : ApiController
word = x.word,
}),
count = qry.Count()
count = count
};

View File

@@ -55,7 +55,7 @@ public class FollowerController : ApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.followers.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.followers.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
/*if (prod != null)
{
@@ -94,9 +94,9 @@ public class FollowerController : ApiController
{
if (!string.IsNullOrEmpty(nums))
{
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.followers.AsEnumerable().Where(q => getDelItem.Contains(q.num)).ToList();
var prod = _db.followers.Where(q => getDelItem.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
foreach (var item in prod)
@@ -139,8 +139,8 @@ public class FollowerController : ApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.follower q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
////var qry = _db.followers.Where(a => a.IsDel == false).AsEnumerable();////不確定是否新增欄位? 先註解
var qry = _db.followers.AsEnumerable();
////var qry = _db.followers.Where(a => a.IsDel == false).AsQueryable();////不確定是否新增欄位? 先註解
var qry = _db.followers.AsQueryable();
if (!string.IsNullOrEmpty(q.f_number))
qry = qry.Where(o => o.f_number.Contains(q.f_number.Trim()));
@@ -151,7 +151,7 @@ public class FollowerController : ApiController
if (q.birthday2.HasValue)
qry = qry.Where(o => o.birthday < Convert.ToDateTime(q.birthday2.Value).AddDays(1));
if (!string.IsNullOrEmpty(q.address))
qry = qry.Where(o => o.address !=null && o.address.Contains(q.address?.Trim()));
qry = qry.Where(o => o.address !=null && o.address.Contains(q.address.Trim()));
//if (q.num.HasValue && q.num.Value>0)
// qry = qry.Where(o => o.num==q.num.Value);
if (q.ept_self.HasValue && q.ept_self.Value )//排除自己
@@ -215,9 +215,10 @@ public class FollowerController : ApiController
var tdesc = publicFun.enum_desc<Model.follower.type>();
var count = qry.Count(); //pageSize = count;//一次取回??
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
f_number = x.f_number,
@@ -339,9 +340,9 @@ public class FollowerController : ApiController
//int ccc = cc.Count();
var qry = _db.followers.AsEnumerable().Where(f => cc.Any(x => x == f.num) || cc.Any(x => x == f.leader));
// 優化:使用子查詢在數據庫層面執行,避免載入所有家族成員 ID
// 生成 SQL: WHERE num IN (SELECT...) OR leader IN (SELECT...)
var qry = _db.followers.Where(f => cc.Contains(f.num) || (f.leader.HasValue && cc.Contains(f.leader.Value)));
@@ -359,11 +360,12 @@ public class FollowerController : ApiController
qry = qry.OrderByDescending(o => o.num);
MyWeb.encrypt encrypt = new MyWeb.encrypt();
var tdesc = publicFun.enum_desc<Model.follower.type>();
int i = 1;
var count = qry.Count(); //pageSize = count;//一次取回??
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
int i = 1;
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
id = i++,
num = x.num,
@@ -524,13 +526,14 @@ public class FollowerController : ApiController
if (_follower > 0)
{
var qry = _db.followers_tablet.AsEnumerable().Where(x=>( x.f_num??0) == _follower);
var qry = _db.followers_tablet.Where(x=> x.f_num == _follower);
qry = qry.OrderByDescending(o => o.num);
var count = qry.Count(); //pageSize = count;//一次取回??
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
int i = 1;
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
id = i++,
num = x.num,
@@ -593,7 +596,7 @@ public class FollowerController : ApiController
[Route("api/follower/tabletDelete/{id}")]
public void tabletDelete(int id)
{
var prod = _db.followers_tablet.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.followers_tablet.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.followers_tablet.Remove(prod);

View File

@@ -52,7 +52,7 @@ public class accountingController : BaseApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.accountings.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.accountings.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
var prod2 = prod.accounting_files;
@@ -89,12 +89,12 @@ public class accountingController : BaseApiController
{
if (!string.IsNullOrEmpty(nums))
{
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.accountings.AsEnumerable().Where(q => getDelItem.Contains(q.num)).ToList();
var prod = _db.accountings.Where(q => getDelItem.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
var prod2 = _db.accounting_files.AsEnumerable().Where(q => q.accounting_num.HasValue && getDelItem.Contains(q.accounting_num.Value)).ToList();
var prod2 = _db.accounting_files.Where(q => q.accounting_num.HasValue && getDelItem.Contains(q.accounting_num.Value)).ToList();
if (prod2.Count() > 0)
{
publicFun publicFun = new publicFun();
@@ -126,7 +126,7 @@ public class accountingController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.accounting q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.accountings.AsEnumerable();
var qry = _db.accountings.AsQueryable();
if (q.category.HasValue)
qry = qry.Where(o => o.category == q.category.Value);
@@ -215,10 +215,12 @@ public class accountingController : BaseApiController
qry = qry.OrderByDescending(o => o.num);
var tdesc = publicFun.enum_desc<Model.accounting.type>();
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
category = x.category,
@@ -245,7 +247,7 @@ public class accountingController : BaseApiController
total =(x.price ?? 0)+( x.tax?? 0),
}),
count = qry.Count()
count = count
};
@@ -259,7 +261,7 @@ public class accountingController : BaseApiController
public IHttpActionResult GetTitleKindList([FromBody] Model.ViewModel.accounting_kind q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.accounting_kind.AsEnumerable();
var qry = _db.accounting_kind.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -297,7 +299,7 @@ public class accountingController : BaseApiController
public IHttpActionResult GetAccountKindList([FromBody] Model.ViewModel.accounting_kind2 q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.accounting_kind2.AsEnumerable();
var qry = _db.accounting_kind2.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -341,15 +343,16 @@ public class accountingController : BaseApiController
if (q.accounting_num.HasValue && q.accounting_num.Value > 0)
{
//檢查
var qry = _db.accounting_files.AsEnumerable();
qry = qry.Where(o => o.accounting_num == q.accounting_num.Value);
qry.OrderByDescending(x => x.num);
var qry = _db.accounting_files.Where(o => o.accounting_num == q.accounting_num.Value);
qry = qry.OrderByDescending(x => x.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
int i = 1;
//已有值
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
id = i++,
num = x.num,
@@ -357,7 +360,7 @@ public class accountingController : BaseApiController
pic1 = x.pic1,
pic1_name = x.pic1_name,
}),
count = qry.Count(),
count = count,
};
if (ret.list == null) throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -473,7 +476,7 @@ public class accountingController : BaseApiController
[Route("api/accounting/DeleteFilesItem/{id}")]//刪除相關檔案
public void DeleteFilesItem(int id)
{
var prod = _db.accounting_files.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.accounting_files.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
if (!string.IsNullOrEmpty(prod.pic1))

View File

@@ -63,17 +63,17 @@ public class activityController : ApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.activities.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.activities.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
//刪除相關資料
var prod2 = _db.activity_relating.AsEnumerable().Where(q => q.activity_num == id).ToList(); //相關項目
var prod2 = _db.activity_relating.Where(q => q.activity_num == id).ToList(); //相關項目
if (prod2.Count > 0)
{
//查詢結果全部刪除
_db.activity_relating.RemoveRange(prod2);
}
var prod3 = _db.activity_spares.AsEnumerable().Where(q => q.activity_num == id).ToList(); //備品項目
var prod3 = _db.activity_spares.Where(q => q.activity_num == id).ToList(); //備品項目
if (prod3.Count > 0)
{
_db.activity_spares.RemoveRange(prod3);
@@ -98,7 +98,7 @@ public class activityController : ApiController
[Route("api/activity/DeleteItem/{id}")]
public void DeleteItem(int id)
{
var prod = _db.actItems.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.actItems.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
/*if (prod != null)
{
//var prod2 = _db.actItem_files.AsEnumerable().Where(q => q.actItem_num == id).ToList(); //相關項目
@@ -144,11 +144,11 @@ public class activityController : ApiController
{
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var prod = _db.activities.AsEnumerable().Where(q => ids.Contains(q.num)).ToList();
var prod = _db.activities.Where(q => ids.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
//var prod2 = _db.activity_relating.AsEnumerable().Where(q => ids.Contains(Convert.ToInt32(q.activity_num))).ToList();
var prod2 = _db.activity_relating.AsEnumerable().Where(q => ids.Any(x => x == q.activity_num)).ToList();
var prod2 = _db.activity_relating.Where(q => ids.Contains(q.activity_num)).ToList();
if (prod2.Count > 0)
{
_db.activity_relating.RemoveRange(prod2);
@@ -156,7 +156,7 @@ public class activityController : ApiController
}
//var prod3 = _db.activity_spares.AsEnumerable().Where(q => ids.Contains(Convert.ToInt32(q.activity_num))).ToList();
var prod3 = _db.activity_spares.AsEnumerable().Where(q => ids.Any(x => x == q.activity_num)).ToList();
var prod3 = _db.activity_spares.Where(q => ids.Contains(q.activity_num)).ToList();
if (prod3.Count > 0)
{
_db.activity_spares.RemoveRange(prod3);
@@ -186,11 +186,11 @@ public class activityController : ApiController
if (!string.IsNullOrEmpty(nums))
{
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var prod = _db.actItems.AsEnumerable().Where(q => ids.Contains(q.num)).ToList();
var prod = _db.actItems.Where(q => ids.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
//var prod2 = _db.actItem_files.AsEnumerable().Where(q => ids.Contains(Convert.ToInt32(q.actItem_num))).ToList();
var prod2 = _db.actItem_files.AsEnumerable().Where(q => ids.Any(x => x == q.actItem_num)).ToList();
var prod2 = _db.actItem_files.Where(q => ids.Contains(q.actItem_num)).ToList();
if (prod2.Count > 0)
{
_db.actItem_files.RemoveRange(prod2);
@@ -281,10 +281,10 @@ public class activityController : ApiController
qry = qry.OrderByDescending(o => o.num);
var count = qry.Count(); //pageSize = count;//一次取回??
if (pageSize > 0) qry = qry.ToPagedList(page, pageSize);
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.Select(x => new
list = qryList.Select(x => new
{
num = x.num,
subject = x.subject,
@@ -319,7 +319,7 @@ public class activityController : ApiController
{
//var stockDt = _db.stocks.AsEnumerable(); ;//庫存
var fileDt = _db.files.AsEnumerable().Where(f => f.subject.Contains(q.fileTxt)).Select(f => f.num.ToString());//文件
var fileDt = _db.files.Where(f => f.subject.Contains(q.fileTxt)).Select(f => f.num.ToString());//文件
//每個品項在每個倉庫的結餘量
var stockDt = (
@@ -364,7 +364,7 @@ public class activityController : ApiController
}
if (!string.IsNullOrEmpty(q.kindTxt))
qry = qry.Where(o => o.actItem_kind?.kind != null && o.actItem_kind.kind.Contains(q.kindTxt));
qry = qry.Where(o => o.actItem_kind != null && o.actItem_kind.kind != null && o.actItem_kind.kind.Contains(q.kindTxt));
if (q.category.HasValue && q.category.Value > 0)
qry = qry.Where(o => o.category == q.category.Value);
if (!string.IsNullOrEmpty(q.categorys))
@@ -410,10 +410,10 @@ public class activityController : ApiController
var tdesc = publicFun.enum_desc<Model.activity.category>();
var count = qry.Count(); //pageSize = count;//一次取回??
if (pageSize > 0) qry = qry.ToPagedList(page, pageSize);
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.Select(x => new
list = qryList.Select(x => new
{
num = x.num,
subject = x.subject,
@@ -711,7 +711,7 @@ public class activityController : ApiController
[Route("api/activity/DeleteFilesItem/{id}")]//刪除品項的相關文件
public void DeleteFilesItem(int id)
{
var prod = _db.actItem_files.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.actItem_files.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -731,16 +731,15 @@ public class activityController : ApiController
if (this_id > 0)
{
//檢查
var qry = _db.activity_kind_detail.AsEnumerable();
qry = qry.Where(o => o.activity_kind_num == this_id);
var qry = _db.activity_kind_detail.Where(o => o.activity_kind_num == this_id);
int i = 1;
//已有值
var count = qry.Count(); //pageSize = count;//一次取回??
if (pageSize > 0) qry = qry.ToPagedList(page, pageSize);
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.Select(x => new
list = qryList.Select(x => new
{
id = i++,
num = x.num,
@@ -830,7 +829,7 @@ public class activityController : ApiController
[Route("api/activity/DeleteActKindDetail/{id}")]//刪除活動分類的detail
public void DeleteActKindDetail(int id)
{
var prod = _db.activity_kind_detail.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.activity_kind_detail.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -849,7 +848,7 @@ public class activityController : ApiController
if (this_act_id > 0)
{
//檢查
var prod = _db.activities.AsEnumerable().Where(o => o.num == this_act_id).FirstOrDefault();
var prod = _db.activities.Where(o => o.num == this_act_id).FirstOrDefault();
if (prod != null)
{
int kind = prod.kind ?? 0; //分類
@@ -927,13 +926,12 @@ public class activityController : ApiController
if (kind > 0)
{
int i = 1;
var qry2 = _db.activity_kind_detail.AsEnumerable();
qry2 = qry2.Where(o => o.activity_kind_num == kind);
var qry2 = _db.activity_kind_detail.Where(o => o.activity_kind_num == kind);
var count2 = qry2.Count(); //pageSize = count2;//一次取回??
if (pageSize > 0) qry2 = qry2.ToPagedList(page, pageSize);
var qry2List = (pageSize > 0) ? qry2.ToPagedList(page, pageSize).ToList() : qry2.ToList();
var ret = new
{
list = qry2.Select(x => new
list = qry2List.Select(x => new
{
id = i++,
num = 0,
@@ -992,7 +990,7 @@ public class activityController : ApiController
public IHttpActionResult GetKindList([FromBody] Model.ViewModel.activity_kind q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.activity_kind.AsEnumerable();
var qry = _db.activity_kind.AsQueryable();
if (q.num.HasValue && q.num > 0)
qry = qry.Where(o => o.num == q.num);
if (!string.IsNullOrEmpty(q.kind))
@@ -1031,7 +1029,7 @@ public class activityController : ApiController
{
if (id > 0)
{
var qry = _db.activity_kind.AsEnumerable();
var qry = _db.activity_kind.AsQueryable();
qry = qry.Where(o => o.num == id);
var prop3 = qry.FirstOrDefault();
if (prop3 != null)
@@ -1151,7 +1149,7 @@ public class activityController : ApiController
[Route("api/activity/DeleteRelatingData/{id}")]//刪除活動的相關資料
public void DeleteRelatingData(int id)
{
var prod = _db.activity_relating.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.activity_relating.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -1169,25 +1167,22 @@ public class activityController : ApiController
if (this_act_id > 0)
{
//檢查
var prod = _db.activities.AsEnumerable().Where(o => o.num == this_act_id).FirstOrDefault();
var prod = _db.activities.Where(o => o.num == this_act_id).FirstOrDefault();
if (prod != null)
{
int kind = prod.kind ?? 0; //分類
var qry1 = _db.activity_spares.AsEnumerable();
qry1 = qry1.Where(o => o.activity_num == this_act_id);
qry1 = qry1.OrderBy(o => o.num);
var qry1 = _db.activity_spares.Where(o => o.activity_num == this_act_id).OrderBy(o => o.num);
var count = qry1.Count(); //pageSize = count;//一次取回??
int i = 1;
if (pageSize > 0) qry1 = qry1.ToPagedList(page, pageSize);
var qry1List = (pageSize > 0) ? qry1.ToPagedList(page, pageSize).ToList() : qry1.ToList();
var ret = new
{
list = qry1.Select(x => new
list = qry1List.Select(x => new
{
id = i++,
num = x.num,
@@ -1278,7 +1273,7 @@ public class activityController : ApiController
[Route("api/activity/DeleteSparesData/{id}")]//刪除活動的備品資料
public void DeleteSparesData(int id)
{
var prod = _db.activity_spares.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.activity_spares.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -1388,10 +1383,10 @@ public class activityController : ApiController
var tdesc = publicFun.enum_desc<Model.activity_check.keyin1>();
var count = qry.Count(); //pageSize = count;//一次取回??
if (pageSize > 0) qry = qry.ToPagedList(page, pageSize);
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.Select(x => new
list = qryList.Select(x => new
{
num = x.num,
subject = x.activity?.subject,
@@ -1416,7 +1411,7 @@ public class activityController : ApiController
[Route("api/activity/DeleteCheck/{id}")]
public void DeleteCheck(int id)
{
var prod = _db.activity_check.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.activity_check.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -1439,7 +1434,7 @@ public class activityController : ApiController
if (!string.IsNullOrEmpty(num[i]))
ids[i] = Convert.ToInt32(num[i]);
}
var prod = _db.activity_check.AsEnumerable().Where(q => ids.Contains(q.num)).ToList();
var prod = _db.activity_check.Where(q => ids.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
@@ -1458,7 +1453,7 @@ public class activityController : ApiController
{
DateTime _now = DateTime.Now;
var qry = _db.activities.AsEnumerable();
var qry = _db.activities.AsQueryable();
if (q.reg_time1.HasValue)
qry = qry.Where(o => o.reg_time >= q.reg_time1.Value);
@@ -1466,7 +1461,7 @@ public class activityController : ApiController
qry = qry.Where(o => o.reg_time < Convert.ToDateTime(q.reg_time2.Value).AddDays(1));
qry = qry.Where(o => (_now >= o.startDate_solar && _now < o.endDate_lunar) || (_now < o.startDate_solar && o.dueDate?.Date.CompareTo(_now) <= 180));
qry = qry.Where(o => (_now >= o.startDate_solar && _now < o.endDate_lunar) || (_now < o.startDate_solar && o.dueDate.HasValue && o.dueDate.Value.Date.CompareTo(_now) <= 180));
qry = qry.OrderByDescending(o => o.num);
@@ -1510,7 +1505,7 @@ public class activityController : ApiController
public IHttpActionResult GetCategoryKindList([FromBody] Model.ViewModel.activity_category_kind q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.activity_category_kind.AsEnumerable();
var qry = _db.activity_category_kind.AsQueryable();
if (q.num.HasValue && q.num > 0)
qry = qry.Where(o => o.num == q.num);
if (!string.IsNullOrEmpty(q.kind))
@@ -1518,10 +1513,10 @@ public class activityController : ApiController
qry = qry.OrderBy(O => O.kind).ThenBy(o => o.num);
var count = qry.Count(); //pageSize = count;//一次取回??
if (pageSize > 0) qry = qry.ToPagedList(page, pageSize);
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.Select(x => new
list = qryList.Select(x => new
{
num = x.num,
kind = x.kind,

View File

@@ -54,7 +54,7 @@ public class activity_kindController : ApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.actItem_kind q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.actItem_kind.AsEnumerable();
var qry = _db.actItem_kind.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
if (!string.IsNullOrEmpty(q.status))

View File

@@ -52,7 +52,7 @@ public class adminUserController : ApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.admins.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.admins.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -76,7 +76,7 @@ public class adminUserController : ApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.admin q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.admins.AsEnumerable();
var qry = _db.admins.AsQueryable();
if (!string.IsNullOrEmpty(q.u_id))
qry = qry.Where(o => o.u_id.Contains(q.u_id));
@@ -90,17 +90,15 @@ public class adminUserController : ApiController
if(q.removeExist.HasValue && q.removeExist.Value)
{
// 優化:使用子查詢在數據庫層面執行,避免載入所有會員的 admin_num
var usedAdminNums = _db.members.Select(b => b.admin_num);
if (q.num.HasValue && q.num.Value > 0)
{
qry = qry.Where(o => (!(from b in _db.members.AsEnumerable()
select b.admin_num)
.Contains(o.num)) || o.num == Convert.ToInt32(q.num.Value));
qry = qry.Where(o => !usedAdminNums.Contains(o.num) || o.num == q.num.Value);
}
else
{
qry = qry.Where(o => (!(from b in _db.members.AsEnumerable()
select b.admin_num)
.Contains(o.num)));
qry = qry.Where(o => !usedAdminNums.Contains(o.num));
}
}
@@ -114,9 +112,12 @@ public class adminUserController : ApiController
else
qry = qry.OrderByDescending(o => o.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
u_id = x.u_id,
@@ -124,7 +125,7 @@ public class adminUserController : ApiController
power = x.power,
}),
count = qry.Count()
count = count
};

View File

@@ -44,19 +44,22 @@ public class appellationController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.appellation q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.appellations.AsEnumerable();
var qry = _db.appellations.AsQueryable();
qry = qry.OrderBy(o => o.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
title = x.title,
}),
count = qry.Count()
count = count
};

View File

@@ -42,7 +42,7 @@ public class bedController : BaseApiController
// DELETE api/<controller>/5
public void Delete(string id)
{
var prod = _db.bed_order.AsEnumerable().Where(q => q.bed_order_no == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.bed_order.Where(q => q.bed_order_no == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
//刪除訂單明細
@@ -80,7 +80,7 @@ public class bedController : BaseApiController
{
order_no = "ED" + DateTime.Now.ToString("yyMMdd");
var qry = _db.companies.AsEnumerable();
var qry = _db.companies.AsQueryable();
var prod = qry.Where(q => q.num == 1).FirstOrDefault();
if (prod != null)
{
@@ -157,16 +157,18 @@ public class bedController : BaseApiController
public IHttpActionResult GetDetailList([FromBody] Model.bed_order q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.bed_order_detail.AsEnumerable();
qry = qry.Where(o => o.bed_order_no == q.bed_order_no);
var qry = _db.bed_order_detail.Where(o => o.bed_order_no == q.bed_order_no);
qry = qry.OrderBy(o => o.checkIn_date ).ThenBy(o => o.num);
int i = 1;
var tdesc = publicFun.enum_desc<Model.bed_kind.bed_type>();
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
id = i++,
num = x.num,
@@ -197,7 +199,7 @@ public class bedController : BaseApiController
},
license = x.license,
}),
count = qry.Count()
count = count
};
return Ok(ret);
@@ -250,7 +252,7 @@ public class bedController : BaseApiController
[Route("api/bed/DeleteBedDetail/{id}")]//刪除detail
public void DeleteBedDetail(int id)
{
var prod = _db.bed_order_detail.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.bed_order_detail.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{

View File

@@ -52,7 +52,7 @@ public class bed_kindController : ApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.bed_kind q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.bed_kind.AsEnumerable();
var qry = _db.bed_kind.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
if (!string.IsNullOrEmpty(q.sex))
@@ -60,16 +60,20 @@ public class bed_kindController : ApiController
if (q.root.HasValue)
qry = qry.Where(o => o.root.Value == q.root.Value);
qry = qry.OrderBy(o => o.range).ThenBy(o => o.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
kind = x.kind,
sex = x.sex,
}),
count = qry.Count()
count = count
};
return Ok(ret);
@@ -83,7 +87,7 @@ public class bed_kindController : ApiController
var tdesc = publicFun.enum_desc<Model.bed_kind.bed_type>();
//var tdesc2 = tdesc.ToArray().Select(x=>x.Value); //[0]單人床,[1]雙人床
//var tdesc3 = tdesc.ToArray().Select(x=>x.Key); //[0]1,[1]2
var qry = _db.bed_kind_detail.AsEnumerable();
var qry = _db.bed_kind_detail.AsQueryable();
if (q.bed_kind_id.HasValue)
qry = qry.Where(o => o.bed_kind_id == q.bed_kind_id);
if (!string.IsNullOrEmpty(q.bed_name))
@@ -103,17 +107,19 @@ public class bed_kindController : ApiController
if (q.inTime.HasValue )
{
//判斷日期沒庫存不能選
var bedDt = _db.bed_order_detail.AsEnumerable().Where(f => f.checkIn_date.HasValue && f.checkIn_date ==q.inTime.Value ).Select(f => f.bed_kind_detail_id.ToString());//掛單表單明細
qry = qry.Where(o => !bedDt.ToArray().Contains(o.num.ToString()));
var bedDt = _db.bed_order_detail.Where(f => f.checkIn_date.HasValue && f.checkIn_date ==q.inTime.Value && f.bed_kind_detail_id.HasValue).Select(f => f.bed_kind_detail_id.Value).ToList();//掛單表單明細
qry = qry.Where(o => !bedDt.Contains(o.num));
}
qry = qry.OrderBy(o => o.bed_name);
int i = 1;
if (pageSize > 0) qry = qry.ToPagedList(page, pageSize);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.Select(x => new
list = qryList.Select(x => new
{
id = i++,
num = x.num,
@@ -122,7 +128,7 @@ public class bed_kindController : ApiController
bed_type_txt = x.bed_type.HasValue? tdesc[x.bed_type ?? 1] : "",
demo = x.demo,
}),
count = qry.Count()
count = count
};
return Ok(ret);
@@ -186,7 +192,7 @@ public class bed_kindController : ApiController
[Route("api/bed_kind/DeleteBedKindDetail/{id}")]//刪除分類的detail
public void DeleteActKindDetail(int id)
{
var prod = _db.bed_kind_detail.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.bed_kind_detail.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{

View File

@@ -44,22 +44,25 @@ public class countryController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.country q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.countries.AsEnumerable();
var qry = _db.countries.AsQueryable();
if (!string.IsNullOrEmpty(q.keyword))
qry = qry.Where(o => o.ID.Contains(q.keyword.Trim()) || o.name_en.Contains(q.keyword.Trim()) || o.name_zh.Contains(q.keyword.Trim()));
qry = qry.OrderBy(o => o.range).ThenBy(o => o.name_en);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
id = x.ID,
name_en = x.name_en,
name_zh = x.name_zh,
}),
count = qry.Count()
count = count
};

View File

@@ -98,7 +98,17 @@ public class familyMembersController : ApiController
[Route("api/familymembers/follower/{followerId}")]
public IHttpActionResult GetByFollower(int followerId)
{
//var originalLazyLoading = _db.Configuration.LazyLoadingEnabled;
//var originalProxyCreation = _db.Configuration.ProxyCreationEnabled;
//
//_db.Configuration.LazyLoadingEnabled = false;
//_db.Configuration.ProxyCreationEnabled = false;
var familyMembers = _db.family_members.Where(fm => fm.follower_num == followerId).ToList();
//_db.Configuration.LazyLoadingEnabled = originalLazyLoading;
//_db.Configuration.ProxyCreationEnabled = originalProxyCreation;
return Ok(familyMembers);
}

View File

@@ -48,7 +48,7 @@ public class memberController : BaseApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.members.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.members.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
if(!string.IsNullOrEmpty(prod.pic1))
@@ -79,7 +79,7 @@ public class memberController : BaseApiController
[Route("api/member/DeleteCheck/{id}")]
public void DeleteCheck(int id) //刪除考勤資料
{
var prod = _db.member_check.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.member_check.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.member_check.Remove(prod);
@@ -93,9 +93,9 @@ public class memberController : BaseApiController
{
if (!string.IsNullOrEmpty(nums))
{
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.members.AsEnumerable().Where(q => getDelItem.Contains(q.num)).ToList();
var prod = _db.members.Where(q => getDelItem.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
publicFun publicFun = new publicFun();
@@ -107,8 +107,8 @@ public class memberController : BaseApiController
}
}
var prod2 = _db.member_check.AsEnumerable().Where(q => getDelItem.Contains(Convert.ToInt32(q.mem_num))).ToList();
if (prod2.Count > 0)
var prod2 = _db.member_check.Where(q => q.mem_num.HasValue && getDelItem.Contains(q.mem_num.Value)).ToList();
if (prod2.Count() > 0)
{
_db.member_check.RemoveRange(prod2);
//_db.SaveChanges();
@@ -135,8 +135,8 @@ public class memberController : BaseApiController
{
if (!string.IsNullOrEmpty(nums))
{
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var prod = _db.member_check.AsEnumerable().Where(q => ids.Contains(q.num)).ToList();
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.member_check.Where(q => ids.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
_db.member_check.RemoveRange(prod);
@@ -150,7 +150,7 @@ public class memberController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.member q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.members.AsEnumerable();
var qry = _db.members.AsQueryable();
if (!string.IsNullOrEmpty(q.u_name))
qry = qry.Where(o => o.u_name.Contains(q.u_name.Trim()));
@@ -220,10 +220,11 @@ public class memberController : BaseApiController
else
qry = qry.OrderByDescending(o => o.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
m_number = x.m_number,
@@ -242,7 +243,7 @@ public class memberController : BaseApiController
starttime = x.group_kind.HasValue ? x.member_group.starttime : (object)null,
demo = x.demo,
}),
count = qry.Count()
count = count
};
@@ -256,7 +257,7 @@ public class memberController : BaseApiController
public IHttpActionResult GetCheckList([FromBody] Model.ViewModel.member_check q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.member_check.AsEnumerable();
var qry = _db.member_check.AsQueryable();
if (!string.IsNullOrEmpty(q.u_name))
qry = qry.Where(o => o.member.u_name.Contains(q.u_name));
@@ -317,9 +318,11 @@ public class memberController : BaseApiController
var tdesc = publicFun.enum_desc<Model.member.attendances.type>();
var tdesc2 = publicFun.enum_desc<Model.member.attendances.login>();
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
m_number = x.member.m_number,
@@ -332,7 +335,7 @@ public class memberController : BaseApiController
login_type_desc = tdesc2[x.login_type ?? 1]
}),
count = qry.Count()
count = count
};
@@ -346,7 +349,7 @@ public class memberController : BaseApiController
public IHttpActionResult GetGroupList([FromBody] Model.ViewModel.member_group q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.member_group.AsEnumerable();
var qry = _db.member_group.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -394,7 +397,7 @@ public class memberController : BaseApiController
public IHttpActionResult GetTitleList([FromBody] Model.ViewModel.member_title q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.member_title.AsEnumerable();
var qry = _db.member_title.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));

View File

@@ -52,7 +52,7 @@ public class member_titleController : ApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.member_title q,
int page, int pageSize = 10, string sortBy = "", bool sortDesc = false)
{
var qry = _db.member_title.AsEnumerable();
var qry = _db.member_title.AsQueryable();
var ret = new { list = "", count = 0 };
return Ok(ret);
}

View File

@@ -49,11 +49,11 @@ public class newsController : BaseApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.news.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.news.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
//刪除檔案
var prod2 = _db.news_files.AsEnumerable().Where(q => q.news_id == id).ToList();
var prod2 = _db.news_files.Where(q => q.news_id == id).ToList();
if (prod2.Count > 0)
{
foreach (var item in prod2)
@@ -91,12 +91,12 @@ public class newsController : BaseApiController
{
if (!string.IsNullOrEmpty(nums))
{
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.news.AsEnumerable().Where(q => getDelItem.Contains(q.num)).ToList();
var prod = _db.news.Where(q => getDelItem.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
var prod2 = _db.news_files.AsEnumerable().Where(q => getDelItem.Contains(q.news_id)).ToList();
var prod2 = _db.news_files.Where(q => getDelItem.Contains(q.news_id)).ToList();
if (prod2.Count > 0)
{
publicFun publicFun = new publicFun();
@@ -139,7 +139,7 @@ public class newsController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.news q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false, bool top = false)
{
var qry = _db.news.AsEnumerable();
var qry = _db.news.AsQueryable();
if (!string.IsNullOrEmpty(q.subject))
qry = qry.Where(o => o.subject.Contains(q.subject.Trim()));
@@ -258,10 +258,13 @@ public class newsController : BaseApiController
else
qry = qry.OrderByDescending(o => o.topping).ThenByDescending(o => o.num);
}
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
subject = x.subject,
@@ -282,7 +285,7 @@ public class newsController : BaseApiController
topping = x.topping
}),
count = qry.Count()
count = count
};
@@ -298,7 +301,7 @@ public class newsController : BaseApiController
{
var qry = _db.news_kind.AsEnumerable();
var qry = _db.news_kind.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -319,17 +322,18 @@ public class newsController : BaseApiController
range = o.range,
}).OrderBy(x => x.root).ThenBy(x => x.kind).ToList(), 0, 0);
var count = qry.Count();
var qry2List = (pageSize > 0) ? qry2.ToPagedList(page, pageSize).ToList() : qry2.ToList();
var ret = new
{
list = qry2.ToPagedList(page, pageSize).Select(x => new
list = qry2List.Select(x => new
{
num = x.num,
kind = new TreeView().RptDash(x.Level) + x.kind,
}),
count = qry.Count()
count = count
};

View File

@@ -99,7 +99,7 @@ public class orderController : ApiController
{
if (id > 0)
{
var prod = _db.pro_order_detail.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.pro_order_detail.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
//刪除掛單表單
@@ -134,7 +134,7 @@ public class orderController : ApiController
{
var ids = nums.TrimEnd(',').Split(',');
var prod = _db.pro_order.AsEnumerable().Where(q => ids.Contains(q.order_no)).ToList();
var prod = _db.pro_order.Where(q => ids.Contains(q.order_no)).ToList();
if (prod.Count() > 0)
{
var prod2 = _db.pro_order_detail.AsEnumerable().Where(q => ids.Contains(Convert.ToString(q.order_no))).ToList();
@@ -201,7 +201,7 @@ public class orderController : ApiController
string sortBy = "", bool sortDesc = false)
{
var qry = _db.pro_order.AsEnumerable();
var qry = _db.pro_order.AsQueryable();
//var aIDt = _db.actItems.AsEnumerable().Where(f => f.subject.Contains(q.actItemTxt.Trim())).Select(f => f.num);//品項
@@ -220,32 +220,32 @@ public class orderController : ApiController
if (!string.IsNullOrEmpty(q.address))
qry = qry.Where(o => o.address.Contains(q.address.Trim()));
if (!string.IsNullOrEmpty(q.subject))
qry = qry.Where(o => o.activity_num.HasValue && o.activity.subject.Contains(q.subject?.Trim()));
qry = qry.Where(o => o.activity_num.HasValue && o.activity.subject.Contains(q.subject.Trim()));
if (!string.IsNullOrEmpty(q.u_name))
qry = qry.Where(o => o.f_num.HasValue && o.follower.u_name.Contains(q.u_name?.Trim()));
qry = qry.Where(o => o.f_num.HasValue && o.follower.u_name.Contains(q.u_name.Trim()));
if (!string.IsNullOrEmpty(q.introducerTxt))
qry = qry.Where(o => o.introducer.HasValue && o.follower1.u_name.Contains(q.introducerTxt?.Trim()));
qry = qry.Where(o => o.introducer.HasValue && o.follower1.u_name.Contains(q.introducerTxt.Trim()));
if (!string.IsNullOrEmpty(q.actItemTxt))
{
//qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.order_no == o.order_no && aIDt.ToArray().Contains(f2.actItem_num?.ToString())).Count() > 0);
// qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.order_no == o.order_no && aIDt.Any(x => x == f2.actItem_num)).Count() > 0);
qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.actItem_num.HasValue && f2.actItem.subject.Contains(q.actItemTxt?.Trim())).Count() > 0);
qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.actItem_num.HasValue && f2.actItem.subject.Contains(q.actItemTxt.Trim())).Count() > 0);
}
if (!string.IsNullOrEmpty(q.country))
qry = qry.Where(o => o.f_num != null && o.follower?.country == q.country);
qry = qry.Where(o => o.f_num != null && o.follower != null && o.follower.country == q.country);
if (!string.IsNullOrEmpty(q.country2))
{
if (q.country2 == "1")
{
qry = qry.Where(o => o.f_num != null && o.follower?.country == "158");
qry = qry.Where(o => o.f_num != null && o.follower != null && o.follower.country == "158");
}
else if (q.country2 == "2")
{
qry = qry.Where(o => o.f_num != null && o.follower?.country != "158");
qry = qry.Where(o => o.f_num != null && o.follower != null && o.follower.country != "158");
}
}
@@ -275,16 +275,16 @@ public class orderController : ApiController
else if (sortBy.Equals("u_name"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.follower?.u_name);
qry = qry.OrderByDescending(o => o.follower != null ? o.follower.u_name : "");
else
qry = qry.OrderBy(o => o.follower?.u_name);
qry = qry.OrderBy(o => o.follower != null ? o.follower.u_name : "");
}
else if (sortBy.Equals("subject"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.activity?.subject);
qry = qry.OrderByDescending(o => o.activity != null ? o.activity.subject : "");
else
qry = qry.OrderBy(o => o.activity?.subject);
qry = qry.OrderBy(o => o.activity != null ? o.activity.subject : "");
}
else
qry = qry.OrderByDescending(o => o.reg_time);
@@ -801,15 +801,15 @@ public class orderController : ApiController
{
//家族 : 家長是我的人,跟我同家長的人,我的家長本人,我本人
var cc = _db.followers.Where(x => x.num == _follower || x.leader == _follower).Select(x => x.num);
var qry = _db.followers.AsEnumerable().Where(f => cc.Any(x => x == f.num) || cc.Any(x => x == f.leader));
var ccList = _db.followers.Where(x => x.num == _follower || x.leader == _follower).Select(x => x.num).ToList();
var qry = _db.followers.Where(f => ccList.Contains(f.num) || (f.leader.HasValue && ccList.Contains(f.leader.Value)));
//未付款資料
var unpayDt = _db.pro_order_detail.AsEnumerable();
var unpayDt = _db.pro_order_detail.AsQueryable();
var coDt = unpayDt.Where(x => x.keyin1 == (int)Model.pro_order.detailKeyin1.Collection);//只列出報名狀態為"收款中"的項目
//未付款訂單
var orderDt = _db.pro_order.AsEnumerable();
var orderDt = _db.pro_order.AsQueryable();
if (!string.IsNullOrEmpty(order_no))
orderDt = orderDt.Where(x => x.order_no == order_no);
orderDt = orderDt.Where(x => coDt.Select(d => d.order_no).Contains(x.order_no));
@@ -855,7 +855,7 @@ public class orderController : ApiController
{
//未付款資料
var unpayDt = _db.pro_order_detail.AsEnumerable();
var unpayDt = _db.pro_order_detail.AsQueryable();
unpayDt = unpayDt.Where(x => x.keyin1 == (int)Model.pro_order.detailKeyin1.Collection);//只列出報名狀態為"收款中"的項目
if (!string.IsNullOrEmpty(q.order_no))
@@ -1118,7 +1118,7 @@ public class orderController : ApiController
[Route("api/order/DeleteRecordDetail/{id}")]//刪除收款註記
public void DeleteRecordDetail(int id)
{
var prod = _db.pro_order_record.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.pro_order_record.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{

View File

@@ -27,14 +27,14 @@ public class orderdetailController:ApiController
//var OrderList = _db.pro_order.Where(u => u.activity_num == activity_num).Select(j => j.order_no).ToList();
//var gdzOrderList = _db.pro_order_detail.Where(o => OrderList.Contains(o.order_no) && o.print_id.Contains("主")).Select(o => o.order_no).Distinct().ToList();
//var qry = _db.pro_order.Where(u => gdzOrderList.Contains(u.order_no)).AsEnumerable();
var qry = _db.pro_order.Where( u => u.activity_num == activity_num).AsEnumerable();
qry = qry.OrderByDescending(o => o.reg_time);
var qry = _db.pro_order.Where( u => u.activity_num == activity_num).OrderByDescending(o => o.reg_time);
var count = qry.Count(); //pageSize = count;//一次取回??
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
order_no = x.order_no,
f_num = x.f_num,
@@ -50,7 +50,7 @@ public class orderdetailController:ApiController
|| u.actItem.subject.Contains("牌")
|| !string.IsNullOrEmpty(u.f_num_tablet))
.Count(),
actItem = x.pro_order_detail.Where(u => u.printed_files != null).FirstOrDefault()?.print_id }
actItem = x.pro_order_detail.Where(u => u.printed_files != null).Select(u => u.print_id).FirstOrDefault() }
}),
count = count
};

View File

@@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web.Http;
using System.Configuration;
/// <summary>
/// pivot01Controller - 法會報名統計分析 API
/// 設計理念:直接查詢 SQL VIEW保持彈性與簡潔
/// </summary>
[ezAuthorize]
public class pivot01Controller : ApiController
{
// 連線字串
private readonly string _connectionString;
public pivot01Controller()
{
// 優先使用 shopConn 連線字串(純 SQL Server 連線字串)
var shopConnectionString = ConfigurationManager.ConnectionStrings["shopConn"]?.ConnectionString;
if (!string.IsNullOrEmpty(shopConnectionString))
{
// 移除不相容的 Provider 參數(包含 SQLOLEDB 和 SQLNCLI11
_connectionString = shopConnectionString
.Replace("Provider=SQLOLEDB;", "")
.Replace("Provider=SQLNCLI11;", "")
.Replace(" Provider=SQLOLEDB", "")
.Replace(" Provider=SQLNCLI11", "");
}
else
{
// 備用方案:從 Entity Framework 連線字串中提取 SQL Server 連線字串
var efConnectionString = ConfigurationManager.ConnectionStrings["ezEntities"]?.ConnectionString;
if (!string.IsNullOrEmpty(efConnectionString))
{
// 解析 EF 連線字串,提取 provider connection string 部分
var startIndex = efConnectionString.IndexOf("provider connection string=&quot;") + "provider connection string=&quot;".Length;
var endIndex = efConnectionString.LastIndexOf("&quot;");
if (startIndex > 0 && endIndex > startIndex)
{
_connectionString = efConnectionString.Substring(startIndex, endIndex - startIndex);
}
else
{
throw new InvalidOperationException("無法解析 Entity Framework 連線字串");
}
}
else
{
throw new InvalidOperationException("找不到可用的資料庫連線字串");
}
}
}
#region VIEW
/// <summary>
/// 執行 SQL 查詢並回傳 DataTable
/// </summary>
/// <param name="sql">SQL 查詢語句</param>
/// <param name="parameters">參數陣列</param>
/// <returns>查詢結果 DataTable</returns>
private DataTable ExecuteSqlQuery(string sql, SqlParameter[] parameters = null)
{
var dataTable = new DataTable();
try
{
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(sql, connection))
{
// 設定逾時時間為 60 秒
command.CommandTimeout = 60;
// 加入參數
if (parameters != null && parameters.Length > 0)
{
command.Parameters.AddRange(parameters);
}
// 開啟連線並執行查詢
connection.Open();
using (var adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataTable);
}
}
}
catch (SqlException ex)
{
throw new Exception($"SQL 查詢錯誤: {ex.Message}", ex);
}
catch (Exception ex)
{
throw new Exception($"執行查詢時發生錯誤: {ex.Message}", ex);
}
return dataTable;
}
/// <summary>
/// DataTable 轉換為動態物件列表(保留中文欄位名)
/// </summary>
/// <param name="dt">DataTable</param>
/// <returns>Dictionary 列表</returns>
private List<Dictionary<string, object>> DataTableToDictionary(DataTable dt)
{
var list = new List<Dictionary<string, object>>();
foreach (DataRow row in dt.Rows)
{
var dict = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
// 保留原始欄位名稱(包含中文)
dict[col.ColumnName] = row[col] == DBNull.Value ? null : row[col];
}
list.Add(dict);
}
return list;
}
#endregion
#region API
/// <summary>
/// GET /api/pivot01/activity_stats
/// 查詢法會統計對應「法會統計」VIEW
/// </summary>
/// <param name="year">查詢年份</param>
/// <returns>法會統計資料</returns>
[HttpGet]
[Route("api/pivot01/activity_stats")]
public IHttpActionResult GetActivityStats(int year)
{
try
{
// 驗證年份參數
if (year < 1900 || year > 2100)
{
return BadRequest("年份參數不正確,請輸入 1900 ~ 2100 之間的年份");
}
// 建立 SQL 查詢(包含當年度及無日期的法會)
string sql = @"
SELECT * FROM [法會統計]
WHERE (YEAR(開始日期) = @year OR 開始日期 IS NULL)
ORDER BY
CASE WHEN 開始日期 IS NULL THEN 1 ELSE 0 END,
開始日期 DESC,
結束日期 DESC
";
// 建立參數
var parameters = new[]
{
new SqlParameter("@year", SqlDbType.Int) { Value = year }
};
// 執行查詢
var dataTable = ExecuteSqlQuery(sql, parameters);
var data = DataTableToDictionary(dataTable);
// 回應結果
var result = new
{
success = true,
data = data,
message = "查詢成功",
rowCount = data.Count
};
return Ok(result);
}
catch (Exception ex)
{
var errorResponse = new
{
success = false,
message = $"查詢失敗:{ex.Message}",
error = ex.ToString()
};
return Content(System.Net.HttpStatusCode.BadRequest, errorResponse);
}
}
/// <summary>
/// GET /api/pivot01/registration_details
/// 查詢報名明細對應「報名明細查詢」VIEW
/// </summary>
/// <param name="activityNum">法會編號(必填)</param>
/// <returns>報名明細資料</returns>
[HttpGet]
[Route("api/pivot01/registration_details")]
public IHttpActionResult GetRegistrationDetails(int? activityNum = null)
{
try
{
// 驗證參數
if (!activityNum.HasValue || activityNum.Value <= 0)
{
return BadRequest("請提供有效的法會編號activityNum");
}
// 建立查詢 SQL
string sql = @"
SELECT * FROM [報名明細查詢]
WHERE 法會ID = @activityNum
ORDER BY 報名日期 DESC, 報名編號 DESC
";
// 建立參數
var parameters = new[]
{
new SqlParameter("@activityNum", SqlDbType.Int) { Value = activityNum.Value }
};
// 執行查詢
var dataTable = ExecuteSqlQuery(sql, parameters);
var data = DataTableToDictionary(dataTable);
// 回應結果
var result = new
{
success = true,
data = data,
message = "查詢成功",
rowCount = data.Count
};
return Ok(result);
}
catch (Exception ex)
{
var errorResponse = new
{
success = false,
message = $"查詢失敗:{ex.Message}",
error = ex.ToString()
};
return Content(System.Net.HttpStatusCode.BadRequest, errorResponse);
}
}
#endregion
}

File diff suppressed because it is too large Load Diff

View File

@@ -48,7 +48,7 @@ public class projectController : BaseApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.projects.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.projects.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.project_sub.RemoveRange(prod.project_sub); //刪除活動資料
@@ -75,7 +75,7 @@ public class projectController : BaseApiController
if (!string.IsNullOrEmpty(nums))
{
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var prod = _db.projects.AsEnumerable().Where(q => ids.Contains(q.num)).ToList();
var prod = _db.projects.Where(q => ids.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
foreach (var item in prod)
@@ -103,7 +103,7 @@ public class projectController : BaseApiController
{
if (id > 0)
{
var prod = _db.project_sub.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.project_sub.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.project_sub.Remove(prod);
@@ -117,7 +117,7 @@ public class projectController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.project q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.projects.AsEnumerable();
var qry = _db.projects.AsQueryable();
if (!string.IsNullOrEmpty(q.subject))
qry = qry.Where(o => o.subject.Contains(q.subject.Trim()));
@@ -192,7 +192,7 @@ public class projectController : BaseApiController
public IHttpActionResult GetKindList([FromBody] Model.ViewModel.project_kind q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.project_kind.AsEnumerable();
var qry = _db.project_kind.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -247,7 +247,7 @@ public class projectController : BaseApiController
if (q.num.HasValue)
{
var qry = _db.project_sub.AsEnumerable();
var qry = _db.project_sub.AsQueryable();
qry = qry.Where(o => o.pro_id == q.num.Value);
@@ -337,11 +337,11 @@ public class projectController : BaseApiController
public IHttpActionResult GetPatronizeList([FromBody] Model.ViewModel.pro_order_detail q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var projectDt = _db.projects.AsEnumerable(); ;//專案
var projectDt = _db.projects.AsQueryable(); ;//專案
try
{
var qry = _db.pro_order_detail.AsEnumerable();
var qry = _db.pro_order_detail.AsQueryable();
qry = qry.Where(o => (int?)o.actItem.category == (int)Model.activity.category.Patronize);
if (q.f_num.HasValue)

View File

@@ -54,7 +54,7 @@ public class stockController : BaseApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.stocks.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.stocks.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
var prod2 = prod.stock_files;
@@ -85,11 +85,11 @@ public class stockController : BaseApiController
{
if (!string.IsNullOrEmpty(nums))
{
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var prod = _db.stocks.AsEnumerable().Where(q => getDelItem.Contains(q.num)).ToList();
var getDelItem = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.stocks.Where(q => getDelItem.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
var prod2 = _db.stock_files.AsEnumerable().Where(q => q.stock_num.HasValue && getDelItem.Contains(q.stock_num.Value)).ToList();
var prod2 = _db.stock_files.Where(q => q.stock_num.HasValue && getDelItem.Contains(q.stock_num.Value)).ToList();
if (prod2.Count() > 0)
{
publicFun publicFun = new publicFun();
@@ -114,7 +114,7 @@ public class stockController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.stock q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.stocks.AsEnumerable();
var qry = _db.stocks.AsQueryable();
if (q.category.HasValue)
qry = qry.Where(o => o.category == q.category.Value);
@@ -198,10 +198,12 @@ public class stockController : BaseApiController
qry = qry.OrderByDescending(o => o.num);
var tdesc = publicFun.enum_desc<Model.stock.type>();
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
category = x.category,
@@ -230,7 +232,7 @@ public class stockController : BaseApiController
price = x.price,
}),
count = qry.Count()
count = count
};
@@ -244,7 +246,7 @@ public class stockController : BaseApiController
public IHttpActionResult GetKindList([FromBody] Model.ViewModel.stock_kind q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.stock_kind.AsEnumerable();
var qry = _db.stock_kind.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -293,7 +295,7 @@ public class stockController : BaseApiController
public IHttpActionResult GetReasonList([FromBody] Model.ViewModel.stock_reason q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.stock_reason.AsEnumerable();
var qry = _db.stock_reason.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -335,15 +337,16 @@ public class stockController : BaseApiController
if (q.stock_num.HasValue && q.stock_num.Value>0)
{
//檢查
var qry = _db.stock_files.AsEnumerable();
qry = qry.Where(o => o.stock_num == q.stock_num.Value);
qry.OrderByDescending(x => x.num);
var qry = _db.stock_files.Where(o => o.stock_num == q.stock_num.Value);
qry = qry.OrderByDescending(x => x.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
int i = 1;
//已有值
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
id = i++,
num = x.num,
@@ -351,7 +354,7 @@ public class stockController : BaseApiController
pic1 = x.pic1,
pic1_name = x.pic1_name,
}),
count = qry.Count(),
count = count,
};
if (ret.list == null) throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -466,7 +469,7 @@ public class stockController : BaseApiController
[Route("api/stock/DeleteFilesItem/{id}")]//刪除相關檔案
public void DeleteFilesItem(int id)
{
var prod = _db.stock_files.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.stock_files.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
if (!string.IsNullOrEmpty(prod.pic1))

View File

@@ -50,7 +50,7 @@ public class supplierController : BaseApiController
// DELETE api/<controller>/5
public void Delete(int id)
{
var prod = _db.suppliers.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.suppliers.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
if (!string.IsNullOrEmpty(prod.pic1))
@@ -80,8 +80,8 @@ public class supplierController : BaseApiController
{
if (!string.IsNullOrEmpty(nums))
{
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s));
var prod = _db.suppliers.AsEnumerable().Where(q => ids.Contains(q.num)).ToList();
var ids = nums.TrimEnd(',').Split(',').Select(s => int.Parse(s)).ToList();
var prod = _db.suppliers.Where(q => ids.Contains(q.num)).ToList();
if (prod.Count() > 0)
{
publicFun publicFun = new publicFun();
@@ -113,7 +113,7 @@ public class supplierController : BaseApiController
public IHttpActionResult GetList([FromBody] Model.ViewModel.supplier q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.suppliers.AsEnumerable();
var qry = _db.suppliers.AsQueryable();
if (!string.IsNullOrEmpty(q.u_name))
qry = qry.Where(o => o.u_name.Contains(q.u_name.Trim()));
@@ -159,9 +159,12 @@ public class supplierController : BaseApiController
else
qry = qry.OrderByDescending(o => o.num);
var count = qry.Count();
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
list = qryList.Select(x => new
{
num = x.num,
u_name = x.u_name,
@@ -176,7 +179,7 @@ public class supplierController : BaseApiController
}).ToList(), x.kind) : "",
}),
count = qry.Count()
count = count
};
@@ -192,7 +195,7 @@ public class supplierController : BaseApiController
{
var qry = _db.supplier_kind.AsEnumerable();
var qry = _db.supplier_kind.AsQueryable();
if (!string.IsNullOrEmpty(q.kind))
qry = qry.Where(o => o.kind.Contains(q.kind));
@@ -205,17 +208,18 @@ public class supplierController : BaseApiController
range = o.range,
}).OrderBy(x => x.root).ThenBy(x => x.kind).ToList(), 0, 0);
var count = qry.Count();
var qry2List = (pageSize > 0) ? qry2.ToPagedList(page, pageSize).ToList() : qry2.ToList();
var ret = new
{
list = qry2.ToPagedList(page, pageSize).Select(x => new
list = qry2List.Select(x => new
{
num = x.num,
kind = new TreeView().RptDash(x.Level) + x.kind,
}),
count = qry.Count()
count = count
};

View File

@@ -237,7 +237,7 @@ public class transfer_registerController : ApiController
x.draft,
follower = x.f_num != null ? _db.followers
.Where(f => f.num == x.f_num)
.AsEnumerable()
.ToList()
.Select(f => new {
f.num,
f.u_name,
@@ -399,6 +399,7 @@ public class transfer_registerController : ApiController
[Route("api/transfer_register/follower_orders")]
public IHttpActionResult GetFollowerOrders(int f_num)
{
// TODO: 優化建議 - 考慮加入分頁或日期範圍限制,避免單一信眾訂單過多時載入大量數據
// 取得該信眾的所有訂單及明細,並關聯活動名稱與品項名稱
var details = _db.pro_order
.Where(o => o.f_num == f_num)
@@ -417,7 +418,7 @@ public class transfer_registerController : ApiController
d.demo,
reg_time = o.reg_time // 報名日期
}))
.AsEnumerable()
.AsEnumerable() // 只在需要內存計算前轉換
.Select(x => new {
x.order_no,
x.activity_num,
@@ -481,7 +482,7 @@ public class transfer_registerController : ApiController
x.draft,
follower = x.f_num != null ? _db.followers
.Where(f => f.num == x.f_num)
.AsEnumerable()
.ToList()
.Select(f => new {
f.num,
f.u_name,
@@ -817,7 +818,7 @@ public class transfer_registerController : ApiController
tr.balance_act_item,
balance_actitem_name = tr.balance_act_item != null ? _db.actItems.Where(a => a.num == tr.balance_act_item).Select(a => a.subject).FirstOrDefault() : ""
})
.AsEnumerable()
.ToList()
.Select(tr => new
{
tr.id,
@@ -1044,6 +1045,9 @@ public class transfer_registerController : ApiController
[Route("api/transfer_register/activity_followers")]
public IHttpActionResult GetActivityFollowers(int activity_num)
{
// TODO: 性能優化 - 大型活動可能有數千筆訂單,建議:
// 1. 在數據庫層面先過濾出未完成沖帳項目(避免載入所有訂單)
// 2. 考慮加入 Take() 限制返回數量,或實作分頁
// 查詢該法會中有報名單且有未完成沖帳項目的信眾
var followers = _db.pro_order
.Where(o => o.activity_num == activity_num)
@@ -1054,7 +1058,7 @@ public class transfer_registerController : ApiController
d.price,
paid = d.pro_order_record.Select(r => r.price).DefaultIfEmpty(0).Sum()
}))
.AsEnumerable()
.AsEnumerable() // 只在需要內存計算前轉換
.Select(x => new {
x.f_num,
x.follower_name,
@@ -1104,7 +1108,7 @@ public class transfer_registerController : ApiController
d.demo,
reg_time = o.reg_time // 報名日期
}))
.AsEnumerable()
.ToList()
.Select(x => new {
x.order_no,
x.activity_num,

View File

@@ -55,17 +55,17 @@ public class apporderController : ApiController
// DELETE api/<controller>/5
public void Delete(string id)
{
var prod = _db.pro_order.AsEnumerable().Where(q => q.order_no == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.pro_order.Where(q => q.order_no == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
//刪除掛單表單
var prod3 = _db.bed_order.AsEnumerable().Where(q => q.order_no == id).ToList();
var prod3 = _db.bed_order.Where(q => q.order_no == id).ToList();
if (prod3.Count > 0)
{
//刪除掛單明細
foreach (var item3 in prod3)
{
var prod4 = _db.bed_order_detail.AsEnumerable().Where(q => q.bed_order_no == item3.bed_order_no).ToList();
var prod4 = _db.bed_order_detail.Where(q => q.bed_order_no == item3.bed_order_no).ToList();
if (prod4.Count > 0)
{
_db.bed_order_detail.RemoveRange(prod4); //查詢結果全部刪除
@@ -97,17 +97,17 @@ public class apporderController : ApiController
{
if (id > 0)
{
var prod = _db.pro_order_detail.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.pro_order_detail.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
//刪除掛單表單
var prod3 = _db.bed_order.AsEnumerable().Where(q => q.order_no == prod.order_no).ToList();
var prod3 = _db.bed_order.Where(q => q.order_no == prod.order_no).ToList();
if (prod3.Count > 0) //count = 1
{
//刪除掛單明細
foreach (var item3 in prod3)
{
var prod4 = _db.bed_order_detail.AsEnumerable().Where(q => q.bed_order_no == item3.bed_order_no).ToList();
var prod4 = _db.bed_order_detail.Where(q => q.bed_order_no == item3.bed_order_no).ToList();
if (prod4.Count > 0)
{
_db.bed_order_detail.RemoveRange(prod4); //查詢結果全部刪除
@@ -132,20 +132,20 @@ public class apporderController : ApiController
{
var ids = nums.TrimEnd(',').Split(',');
var prod = _db.pro_order.AsEnumerable().Where(q => ids.Contains(q.order_no)).ToList();
var prod = _db.pro_order.Where(q => ids.Contains(q.order_no)).ToList();
if (prod.Count() > 0)
{
var prod2 = _db.pro_order_detail.AsEnumerable().Where(q => ids.Contains(Convert.ToString(q.order_no))).ToList();
var prod2 = _db.pro_order_detail.Where(q => ids.Contains(Convert.ToString(q.order_no))).ToList();
if (prod2.Count > 0)
{
foreach (var item2 in prod2)
{
var prod3 = _db.bed_order.AsEnumerable().Where(q => q.order_no == item2.order_no && q.o_detail_id == item2.num).ToList();
var prod3 = _db.bed_order.Where(q => q.order_no == item2.order_no && q.o_detail_id == item2.num).ToList();
if (prod3.Count > 0)
{
foreach (var item3 in prod3)
{
var prod4 = _db.bed_order_detail.AsEnumerable().Where(q => q.bed_order_no == item3.bed_order_no).ToList();
var prod4 = _db.bed_order_detail.Where(q => q.bed_order_no == item3.bed_order_no).ToList();
if (prod4.Count > 0)
{
_db.bed_order_detail.RemoveRange(prod4);
@@ -156,8 +156,8 @@ public class apporderController : ApiController
}
//var prod32 = _db.bed_order.AsEnumerable().Where(q => q.order_no == item2.order_no && q.o_detail_id == item2.num).ToList();
//var prod42 = _db.bed_order_detail.AsEnumerable().Where(q => prod32.Select(qf => qf.order_no).ToArray().Contains(q.bed_order_no)).ToList();
//var prod32 = _db.bed_order.Where(q => q.order_no == item2.order_no && q.o_detail_id == item2.num).ToList();
//var prod42 = _db.bed_order_detail.Where(q => prod32.Select(qf => qf.order_no).ToArray().Contains(q.bed_order_no)).ToList();
//_db.bed_order_detail.RemoveRange(prod42);
//_db.bed_order.RemoveRange(prod32);
@@ -200,7 +200,7 @@ public class apporderController : ApiController
{
var qry = _db.pro_order.AsEnumerable();
//var aIDt = _db.actItems.AsEnumerable().Where(f => f.subject.Contains(q.actItemTxt.Trim())).Select(f => f.num);//品項
//var aIDt = _db.actItems.Where(f => f.subject.Contains(q.actItemTxt.Trim())).Select(f => f.num);//品項
if (!string.IsNullOrEmpty(q.order_no))
@@ -218,32 +218,32 @@ public class apporderController : ApiController
if (!string.IsNullOrEmpty(q.address))
qry = qry.Where(o => o.address.Contains(q.address.Trim()));
if (!string.IsNullOrEmpty(q.subject))
qry = qry.Where(o => o.activity_num.HasValue && o.activity.subject.Contains(q.subject?.Trim()));
qry = qry.Where(o => o.activity_num.HasValue && o.activity.subject.Contains(q.subject.Trim()));
if (!string.IsNullOrEmpty(q.u_name))
qry = qry.Where(o => o.f_num.HasValue && o.follower.u_name.Contains(q.u_name?.Trim()));
qry = qry.Where(o => o.f_num.HasValue && o.follower.u_name.Contains(q.u_name.Trim()));
if (!string.IsNullOrEmpty(q.introducerTxt))
qry = qry.Where(o => o.introducer.HasValue && o.follower1.u_name.Contains(q.introducerTxt?.Trim()));
qry = qry.Where(o => o.introducer.HasValue && o.follower1.u_name.Contains(q.introducerTxt.Trim()));
if (!string.IsNullOrEmpty(q.actItemTxt))
{
//qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.order_no == o.order_no && aIDt.ToArray().Contains(f2.actItem_num?.ToString())).Count() > 0);
// qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.order_no == o.order_no && aIDt.Any(x => x == f2.actItem_num)).Count() > 0);
qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.actItem_num.HasValue && f2.actItem.subject.Contains(q.actItemTxt?.Trim())).Count() > 0);
qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.actItem_num.HasValue && f2.actItem.subject.Contains(q.actItemTxt.Trim())).Count() > 0);
}
if (!string.IsNullOrEmpty(q.country))
qry = qry.Where(o => o.f_num != null && o.follower?.country == q.country);
qry = qry.Where(o => o.f_num != null && o.follower != null && o.follower.country == q.country);
if (!string.IsNullOrEmpty(q.country2))
{
if (q.country2 == "1")
{
qry = qry.Where(o => o.f_num != null && o.follower?.country == "158");
qry = qry.Where(o => o.f_num != null && o.follower != null && o.follower.country == "158");
}
else if (q.country2 == "2")
{
qry = qry.Where(o => o.f_num != null && o.follower?.country != "158");
qry = qry.Where(o => o.f_num != null && o.follower != null && o.follower.country != "158");
}
}
@@ -317,11 +317,11 @@ public class apporderController : ApiController
if (!string.IsNullOrEmpty( order_no ))
{
//檢查
var prod = _db.pro_order.AsEnumerable().Where(o => o.order_no == order_no).FirstOrDefault();
var prod = _db.pro_order.Where(o => o.order_no == order_no).FirstOrDefault();
if (prod != null)
{
int activity = prod.activity_num.HasValue ? prod.activity_num.Value : 0; //活動
var bedDt = _db.bed_order_detail.AsEnumerable().Where(b=>b.bed_order.order_no== order_no ); ;//掛單明細
var bedDt = _db.bed_order_detail.Where(b=>b.bed_order.order_no== order_no ); ;//掛單明細
//var qry1 = _db.pro_order_detail.AsEnumerable();
@@ -435,7 +435,7 @@ public class apporderController : ApiController
if (activity > 0)
{
i = 1;
var qry2 = _db.activity_relating.AsEnumerable();
var qry2 = _db.activity_relating.AsQueryable();
qry2 = qry2.Where(o => o.activity_num == activity);
if (q.actItem_kind_num.HasValue)
@@ -690,14 +690,14 @@ public class apporderController : ApiController
//家族 : 家長是我的人,跟我同家長的人,我的家長本人,我本人
var cc = _db.followers.Where(x => x.num == _follower || x.leader == _follower).Select(x => x.num);
var qry = _db.followers.AsEnumerable().Where(f => cc.Any(x => x == f.num) || cc.Any(x => x == f.leader));
var qry = _db.followers.Where(f => cc.Any(x => x == f.num) || cc.Any(x => x == f.leader));
//未付款資料
var unpayDt = _db.pro_order_detail.AsEnumerable();
var unpayDt = _db.pro_order_detail.AsQueryable();
var coDt = unpayDt.Where(x => x.keyin1 == (int)Model.pro_order.detailKeyin1.Collection);//只列出報名狀態為"收款中"的項目
//未付款訂單
var orderDt = _db.pro_order.AsEnumerable();
var orderDt = _db.pro_order.AsQueryable();
if (!string.IsNullOrEmpty(order_no))
orderDt = orderDt.Where(x => x.order_no == order_no);
orderDt = orderDt.Where(x => coDt.Select(d => d.order_no).Contains(x.order_no));
@@ -737,7 +737,7 @@ public class apporderController : ApiController
{
//未付款資料
var unpayDt = _db.pro_order_detail.AsEnumerable();
var unpayDt = _db.pro_order_detail.AsQueryable();
unpayDt = unpayDt.Where(x => x.keyin1 == (int)Model.pro_order.detailKeyin1.Collection);//只列出報名狀態為"收款中"的項目
if (!string.IsNullOrEmpty(q.order_no))
@@ -865,10 +865,10 @@ public class apporderController : ApiController
if (u_name >0 && (!string.IsNullOrEmpty(q.order_no)))
{
//檢查
var prod = _db.pro_order.AsEnumerable().Where(o => o.f_num == u_name && o.order_no != q.order_no).ToList();
var prod = _db.pro_order.Where(o => o.f_num == u_name && o.order_no != q.order_no).ToList();
if (prod.Count > 0)
{
var qry = _db.pro_order_detail.AsEnumerable().Where(o => prod.Any(x => x.order_no==o.order_no) );
var qry = _db.pro_order_detail.Where(o => prod.Any(x => x.order_no==o.order_no) );
int i = 1;
var ret = new
{
@@ -942,7 +942,7 @@ public class apporderController : ApiController
if (detail_num > 0 )
{
//檢查
var prod = _db.pro_order_record.AsEnumerable().Where(o => o.detail_num == detail_num )
var prod = _db.pro_order_record.Where(o => o.detail_num == detail_num )
.OrderByDescending(o=>o.pay_date).ToList();
int i = 1;
var ret = new
@@ -979,7 +979,7 @@ public class apporderController : ApiController
[Route("api/apporder/DeleteRecordDetail/{id}")]//刪除收款註記
public void DeleteRecordDetail(int id)
{
var prod = _db.pro_order_record.AsEnumerable().Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
var prod = _db.pro_order_record.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -995,8 +995,8 @@ public class apporderController : ApiController
{
if (item.detail_num > 0)
{
float total = _db.pro_order_detail.AsEnumerable().Where(x => x.num == item.detail_num).Select(x => x.price.Value * x.qty.Value).Sum();
float pay = _db.pro_order_record.AsEnumerable().Where(x => x.detail_num == item.detail_num).Select(x => x.price.Value).Sum();
float total = _db.pro_order_detail.Where(x => x.num == item.detail_num).Select(x => x.price.Value * x.qty.Value).Sum();
float pay = _db.pro_order_record.Where(x => x.detail_num == item.detail_num).Select(x => x.price.Value).Sum();
if (pay + item.price.Value <= total)
{

View File

@@ -130,7 +130,8 @@ public partial class admin_hr_import : MyWeb.config
);
sd2.AppendChild(tr);
//查詢要匯出的資料
var list = _db.member_group.AsEnumerable().ToList();
// TODO: REVIEW - member_group 為固定少量資料表ToList() 可接受
var list = _db.member_group.ToList();
if (list.Count > 0)
{
foreach (var item in list)
@@ -178,7 +179,8 @@ public partial class admin_hr_import : MyWeb.config
);
sd3.AppendChild(tr);
//查詢要匯出的資料
var list2 = _db.member_title.AsEnumerable().ToList();
// TODO: REVIEW - member_title 為固定少量資料表ToList() 可接受
var list2 = _db.member_title.ToList();
if (list2.Count > 0)
{
foreach (var item in list2)
@@ -229,10 +231,16 @@ public partial class admin_hr_import : MyWeb.config
startRowNumber += 1;
}
var fDt = _db.followers.AsEnumerable().ToList(); //信眾
var aDt = _db.admins.AsEnumerable().ToList(); //後台系統
var gDt = _db.member_group.AsEnumerable().Select(x => x.num).ToList(); //組別
var tDt = _db.member_title.AsEnumerable().Select(x => x.num).ToList(); //職稱
// TODO: REVIEW - 整表載入性能評估:
// - followers (信眾) ⚠️ 會成長!若超過 1000 筆需優化為按需載入(先掃描 Excel 收集編號)
// - admins (管理員) ✓ 通常少量ToList() 可接受
// - member_group (組別) ✓ 固定少量,可接受
// - member_title (職稱) ✓ 固定少量,可接受
// 優化方案:先掃描 Excel → 只載入需要的編號 → 使用 Dictionary 加速查找O(1) vs O(n)
var fDt = _db.followers.ToList(); //信眾
var aDt = _db.admins.ToList(); //後台系統
var gDt = _db.member_group.Select(x => x.num).ToList(); //組別
var tDt = _db.member_title.Select(x => x.num).ToList(); //職稱
MyWeb.encrypt encrypt = new MyWeb.encrypt();
for (int currentRow = startRowNumber; currentRow <= endRowNumber; currentRow++)

View File

@@ -27,7 +27,6 @@ public partial class admin_hr_kind_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var qry = _db.member_group.AsEnumerable();
var prod = _db.member_group.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -70,7 +69,6 @@ public partial class admin_hr_kind_reg : MyWeb.config
protected void TreeTopology()
{
var qry = _db.member_group.AsEnumerable();
var prod = _db.member_group.ToList();
//treeDt = prod.CopyToDataTable();
@@ -235,7 +233,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
try
{
var prod = _db.member_group.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.member_group.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -281,7 +279,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
del_product(num);
var prod = _db.member_group.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.member_group.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.member_group.Remove(prod);
@@ -302,7 +300,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.member_group.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.member_group.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -319,7 +317,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
}
public void del_product(int num)
{
var prod = _db.members.AsEnumerable().Where(q => q.group_kind == num).ToList();
var prod = _db.members.Where(q => q.group_kind == num).ToList();
if (prod.Count > 0)
{
//清空組別分類

View File

@@ -22,7 +22,6 @@ public partial class admin_hr_kind_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var qry = _db.member_title.AsEnumerable();
var prod = _db.member_title.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -207,7 +206,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
}
try
{
var prod = _db.member_title.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.member_title.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -248,7 +247,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
int num = Val(Request["num"]);
del_product(num);
var prod = _db.member_title.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.member_title.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.member_title.Remove(prod);
@@ -269,7 +268,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.member_title.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.member_title.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -286,7 +285,7 @@ public partial class admin_hr_kind_reg : MyWeb.config
public void del_product(int num)
{
var prod = _db.members.AsEnumerable().Where(q => q.title_kind == num).ToList();
var prod = _db.members.Where(q => q.title_kind == num).ToList();
if (prod.Count > 0)
{
//清空分類

View File

@@ -21,7 +21,7 @@ public partial class admin_accounting_kind_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.accounting_kind.AsEnumerable().Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
var prod = _db.accounting_kind.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -215,7 +215,7 @@ public partial class admin_accounting_kind_reg : MyWeb.config
try
{
var prod = _db.accounting_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.accounting_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -254,7 +254,7 @@ public partial class admin_accounting_kind_reg : MyWeb.config
int num = Val(Request["num"]);
del_product(num);
var prod = _db.accounting_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.accounting_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.accounting_kind.Remove(prod);
@@ -272,7 +272,7 @@ public partial class admin_accounting_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.accounting_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.accounting_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -291,7 +291,7 @@ public partial class admin_accounting_kind_reg : MyWeb.config
public void del_product(int num)
{
var prod = _db.accountings.AsEnumerable().Where(q => q.kind == num).ToList();
var prod = _db.accountings.Where(q => q.kind == num).ToList();
if (prod.Count > 0)
{
////查詢結果全部刪除

View File

@@ -21,7 +21,7 @@ public partial class admin_accounting_kind_reg2 : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.accounting_kind2.AsEnumerable().Where(q => q.num == _num).FirstOrDefault();
var prod = _db.accounting_kind2.Where(q => q.num == _num).FirstOrDefault();
if (prod != null)
{
@@ -228,7 +228,7 @@ public partial class admin_accounting_kind_reg2 : MyWeb.config
try
{
var prod = _db.accounting_kind2.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.accounting_kind2.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -271,7 +271,7 @@ public partial class admin_accounting_kind_reg2 : MyWeb.config
int num = Val(Request["num"]);
del_product(num);
var prod = _db.accounting_kind2.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.accounting_kind2.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.accounting_kind2.Remove(prod);
@@ -288,7 +288,7 @@ public partial class admin_accounting_kind_reg2 : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.accounting_kind2.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.accounting_kind2.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -306,7 +306,7 @@ public partial class admin_accounting_kind_reg2 : MyWeb.config
public void del_product(int num)
{
var prod = _db.accountings.AsEnumerable().Where(q => q.kind2 == num).ToList();
var prod = _db.accountings.Where(q => q.kind2 == num).ToList();
if (prod.Count > 0)
{
////查詢結果全部刪除

View File

@@ -21,7 +21,7 @@ public partial class admin_activity_itemKind : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.actItem_kind.AsEnumerable().Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
var prod = _db.actItem_kind.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -214,7 +214,7 @@ public partial class admin_activity_itemKind : MyWeb.config
try
{
var prod = _db.actItem_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.actItem_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -252,7 +252,7 @@ public partial class admin_activity_itemKind : MyWeb.config
int num = Val(Request["num"]);
del_product(num);
var prod = _db.actItem_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.actItem_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
@@ -271,7 +271,7 @@ public partial class admin_activity_itemKind : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.actItem_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.actItem_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -289,7 +289,7 @@ public partial class admin_activity_itemKind : MyWeb.config
public void del_product(int num)
{
var prod = _db.actItems.AsEnumerable().Where(q => q.kind == num).ToList();
var prod = _db.actItems.Where(q => q.kind == num).ToList();
if (prod.Count > 0)
{
//清空分類

View File

@@ -24,7 +24,7 @@ public partial class admin_activity_kind_reg : MyWeb.config
{
_this_id = ValString(Request["num"]);
int _num = Val(Request["num"]);
var prod = _db.activity_kind.AsEnumerable().Where(q => q.num == _num).OrderBy(q=>q.kind).FirstOrDefault();
var prod = _db.activity_kind.Where(q => q.num == _num).OrderBy(q=>q.kind).FirstOrDefault();
if (prod != null)
{
@@ -230,7 +230,7 @@ public partial class admin_activity_kind_reg : MyWeb.config
try
{
var prod = _db.activity_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.activity_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -284,7 +284,7 @@ public partial class admin_activity_kind_reg : MyWeb.config
{
int num = Val(Request["num"]);
del_product(num);
var prod = _db.activity_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.activity_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.activity_kind.Remove(prod);
@@ -301,7 +301,7 @@ public partial class admin_activity_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.activity_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.activity_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -317,7 +317,7 @@ public partial class admin_activity_kind_reg : MyWeb.config
}
public void del_product(int num)
{
var prod = _db.activities.AsEnumerable().Where(q => q.kind == num).ToList();
var prod = _db.activities.Where(q => q.kind == num).ToList();
if (prod.Count > 0)
{
//清空分類

View File

@@ -21,7 +21,7 @@ public partial class admin_activity_kind_reg2 : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.activity_category_kind.AsEnumerable().Where(q => q.num == _num).Where(q => q.num == _num).FirstOrDefault();
var prod = _db.activity_category_kind.Where(q => q.num == _num).FirstOrDefault();
if (prod != null)
{
@@ -208,7 +208,7 @@ public partial class admin_activity_kind_reg2 : MyWeb.config
try
{
var prod = _db.activity_category_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.activity_category_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -247,7 +247,7 @@ public partial class admin_activity_kind_reg2 : MyWeb.config
del_product(num);
var prod = _db.activity_category_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.activity_category_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.activity_category_kind.Remove(prod);
@@ -265,7 +265,7 @@ public partial class admin_activity_kind_reg2 : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.activity_category_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.activity_category_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -284,7 +284,7 @@ public partial class admin_activity_kind_reg2 : MyWeb.config
public void del_product(int num) //刪除訊息
{
//EF Model 資料庫拉關聯 , 故刪除類型前,一定要先刪除訊息,否則關聯會出錯
var prod = _db.activities.AsEnumerable().Where(q => q.category_kind == num).ToList();
var prod = _db.activities.Where(q => q.category_kind == num).ToList();
if (prod.Count > 0)
{

View File

@@ -30,7 +30,7 @@ public partial class admin_bed_kind_reg : MyWeb.config
{
_this_id = ValString(Request["num"]);
int _num = Val(Request["num"]);
var prod = _db.bed_kind.AsEnumerable().Where(q => q.num == _num).FirstOrDefault();
var prod = _db.bed_kind.Where(q => q.num == _num).FirstOrDefault();
if (prod != null)
{
@@ -250,7 +250,7 @@ public partial class admin_bed_kind_reg : MyWeb.config
try
{
var prod = _db.bed_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.bed_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -293,7 +293,7 @@ public partial class admin_bed_kind_reg : MyWeb.config
{
int num = Val(Request["num"]);
del_product(num);
var prod = _db.bed_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.bed_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.bed_kind.Remove(prod);
@@ -310,7 +310,7 @@ public partial class admin_bed_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.bed_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.bed_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -327,14 +327,14 @@ public partial class admin_bed_kind_reg : MyWeb.config
public void del_product(int num)
{
//床位資料
var prod = _db.bed_kind_detail.AsEnumerable().Where(q => q.bed_kind_id == num).ToList();
var prod = _db.bed_kind_detail.Where(q => q.bed_kind_id == num).ToList();
if (prod.Count > 0)
{
//清空分類
foreach (var item in prod)
{
//掛單資料
var prod2 = _db.bed_order_detail.AsEnumerable().Where(q => q.bed_kind_detail_id == item.num).ToList();
var prod2 = _db.bed_order_detail.Where(q => q.bed_kind_detail_id == item.num).ToList();
if (prod2.Count > 0)
{
//清空分類
@@ -349,7 +349,7 @@ public partial class admin_bed_kind_reg : MyWeb.config
}
//掛單資料
var prod3 = _db.bed_order_detail.AsEnumerable().Where(q => q.bed_kind1 == num || q.bed_kind2 == num).ToList();
var prod3 = _db.bed_order_detail.Where(q => q.bed_kind1 == num || q.bed_kind2 == num).ToList();
if (prod3.Count > 0)
{
//清空分類

View File

@@ -42,7 +42,7 @@ public partial class admin_follower_index : MyWeb.config
//國籍
s_country.Items.Clear();
s_country.Items.Add(new ListItem("請選擇", ""));
var qry =_db.countries.AsEnumerable().OrderBy(x => x.range).ThenBy(x => x.name_en).ToList();
var qry =_db.countries.OrderBy(x => x.range).ThenBy(x => x.name_en).ToList();
if (qry.Count > 0)
{
foreach(var x in qry)
@@ -222,7 +222,7 @@ public partial class admin_follower_index : MyWeb.config
{
//查詢要匯出的資料
var qry = _db.followers.AsEnumerable();
var qry = _db.followers.AsQueryable();
//紀錄匯出條件
if (!isStrNull(s_f_number.Value))

View File

@@ -25,7 +25,7 @@ public partial class admin_follower_print_ : System.Web.UI.Page
//紀錄匯出條件
string _query = "";
var qry = _db.followers.AsEnumerable();
var qry = _db.followers.AsQueryable();
if (!string.IsNullOrEmpty(Request["f_number"]))
{
qry = qry.Where(o => o.f_number.Contains(Request["f_number"].Trim()));
@@ -57,7 +57,7 @@ public partial class admin_follower_print_ : System.Web.UI.Page
if (!string.IsNullOrEmpty(Request["country"]))
{
qry = qry.Where(o => o.country == Request["country"]);
_query += "國家:" + (_db.countries.AsEnumerable().Where(x => x.ID == Request["country"].ToString()).Select(x => x.name_zh).FirstOrDefault()??"" )+ "\n";
_query += "國家:" + (_db.countries.Where(x => x.ID == Request["country"].ToString()).Select(x => x.name_zh).FirstOrDefault()??"" )+ "\n";
}
if (!string.IsNullOrEmpty(Request["country2"]))
{
@@ -70,7 +70,7 @@ public partial class admin_follower_print_ : System.Web.UI.Page
qry = qry.Where(o => o.country != "158");
}
_query += "國家:" + (_db.countries.AsEnumerable().Where(x => x.ID == Request["country2"].ToString()).Select(x => x.name_zh).FirstOrDefault()??"") + "\n";
_query += "國家:" + (_db.countries.Where(x => x.ID == Request["country2"].ToString()).Select(x => x.name_zh).FirstOrDefault()??"") + "\n";
}
//管理報表

View File

@@ -1393,7 +1393,7 @@
message: `確定要將 ${guest.follower.u_name || ''} 退房嗎?`,
onConfirm: async () => {
try {
const response = await axios.post(`/api/guadanorderguest/checkout`, null, {
const response = await axios.post(HTTP_HOST + HTTP_HOST + `api/guadanorderguest/checkout`, null, {
params: { uuid: guest.uuid }
});
@@ -1594,7 +1594,7 @@
onConfirm: async () => {
try {
// 發送請求到後端 API
const response = await axios.post(`/api/guadanorderguest/checkin`, null, {
const response = await axios.post(HTTP_HOST + `api/guadanorderguest/checkin`, null, {
params: { uuid: guest.uuid }
});

View File

@@ -21,7 +21,7 @@ public partial class admin_news_kind_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.news_kind.AsEnumerable().Where(q => q.num == _num).OrderBy(q=>q.kind).FirstOrDefault();
var prod = _db.news_kind.Where(q => q.num == _num).OrderBy(q=>q.kind).FirstOrDefault();
if (prod != null)
{
@@ -214,7 +214,7 @@ public partial class admin_news_kind_reg : MyWeb.config
try
{
var prod = _db.news_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.news_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -255,7 +255,7 @@ public partial class admin_news_kind_reg : MyWeb.config
del_product(num);
var prod = _db.news_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.news_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.news_kind.Remove(prod);
@@ -273,7 +273,7 @@ public partial class admin_news_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.news_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.news_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -292,7 +292,7 @@ public partial class admin_news_kind_reg : MyWeb.config
public void del_product(int num) //刪除訊息
{
//EF Model 資料庫拉關聯 , 故刪除分類前,一定要先刪除訊息,否則關聯會出錯
var prod = _db.news.AsEnumerable().Where(q => q.kind == num).ToList();
var prod = _db.news.Where(q => q.kind == num).ToList();
if (prod.Count > 0)
{
////刪除檔案

View File

@@ -27,7 +27,7 @@ public partial class admin_news_news_reg : MyWeb.config
else
{
int _num = Val(Request["num"]);
var qry = _db.news.AsEnumerable();
var qry = _db.news.AsQueryable();
//var prod = _db.news.Where(q => q.num == _num).ToList();
var prod = qry.Where(q => q.num == _num).FirstOrDefault();
@@ -410,13 +410,11 @@ public partial class admin_news_news_reg : MyWeb.config
#region
protected void initNewsFiles(int num = 0)
{
var qry = _db.news_files.AsEnumerable();
var qry = _db.news_files.AsQueryable();
if (num > 0)
qry = qry.Where(q => q.news_id == num).ToList();
else
qry = qry.ToList();
qry = qry.Where(q => q.news_id == num);
fileRepeater.DataSource = qry;
fileRepeater.DataSource = qry.ToList();
fileRepeater.DataBind();
}
protected void fileRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)

View File

@@ -47,7 +47,7 @@ public partial class admin_order_index : MyWeb.config
//國籍
s_country.Items.Clear();
s_country.Items.Add(new ListItem("請選擇", ""));
var qry = _db.countries.AsEnumerable().OrderBy(x => x.range).ThenBy(x => x.name_en).ToList();
var qry = _db.countries.OrderBy(x => x.range).ThenBy(x => x.name_en).ToList();
if (qry.Count > 0)
{
foreach (var x in qry)
@@ -128,8 +128,8 @@ public partial class admin_order_index : MyWeb.config
sd.AppendChild(tr);
//查詢要匯出的資料
var aIDt = _db.actItems.AsEnumerable().Where(f => f.subject.Contains(s_actItemTxt.Value.Trim())).Select(f => f.num.ToString());//品項
var qry = _db.pro_order.AsEnumerable();
var aIDt = _db.actItems.Where(f => f.subject.Contains(s_actItemTxt.Value.Trim())).Select(f => f.num.ToString());//品項
var qry = _db.pro_order.AsQueryable();
if (!isStrNull(s_order_no.Value))
qry = qry.Where(o => o.order_no.Contains(s_order_no.Value.Trim()));
@@ -154,7 +154,7 @@ public partial class admin_order_index : MyWeb.config
MyWeb.encrypt encrypt = new MyWeb.encrypt();
var tdesc = publicFun.enum_desc<Model.pro_order.detailKeyin1>();
var bedDt = _db.bed_order_detail.AsEnumerable();//掛單明細
var bedDt = _db.bed_order_detail.AsQueryable();//掛單明細
//left join 使用 GroupJoin
@@ -167,7 +167,7 @@ public partial class admin_order_index : MyWeb.config
order_no = o.order_no,
up_time = o.up_time,
keyin1 = o.keyin1,
f_num = o.follower?.u_name, //姓名/名稱
f_num = o.follower != null ? o.follower.u_name : "", //姓名/名稱
phone = o.phone,
activity_num = o.activity_num.HasValue ? o.activity.subject : "",
address = o.address,

View File

@@ -25,7 +25,7 @@ public partial class admin_follower_print_ : System.Web.UI.Page
//紀錄匯出條件
string _query = "";
var qry = _db.pro_order.AsEnumerable();
var qry = _db.pro_order.AsQueryable();
if (!string.IsNullOrEmpty(Request["order_no"]))
{
qry = qry.Where(o => o.order_no.Contains(Request["order_no"].Trim()));
@@ -73,21 +73,21 @@ public partial class admin_follower_print_ : System.Web.UI.Page
}
if (!string.IsNullOrEmpty(Request["country"]))
{
qry = qry.Where(o => o.f_num != null && o.follower?.country == Request["country"]);
_query += "國家:" + (_db.countries.AsEnumerable().Where(x => x.ID == Request["country"]).Select(x => x.name_zh).FirstOrDefault()??"") + "\n";
qry = qry.Where(o => o.f_num != null && o.follower.country == Request["country"]);
_query += "國家:" + (_db.countries.Where(x => x.ID == Request["country"]).Select(x => x.name_zh).FirstOrDefault()??"") + "\n";
}
if (!string.IsNullOrEmpty(Request["country2"]))
{
if (Request["country2"] == "1")
{
qry = qry.Where(o => o.f_num != null && o.follower?.country == "158");
qry = qry.Where(o => o.f_num != null && o.follower.country == "158");
}
else if (Request["country2"] == "2")
{
qry = qry.Where(o => o.f_num != null && o.follower?.country != "158");
qry = qry.Where(o => o.f_num != null && o.follower.country != "158");
}
_query += "國家:" + (_db.countries.AsEnumerable().Where(x => x.ID == Request["country2"]).Select(x => x.name_zh).FirstOrDefault()??"") + "\n";
_query += "國家:" + (_db.countries.Where(x => x.ID == Request["country2"]).Select(x => x.name_zh).FirstOrDefault()??"") + "\n";
}
if (!string.IsNullOrEmpty(Request["hasPrice"]))
{
@@ -175,7 +175,7 @@ public partial class admin_follower_print_ : System.Web.UI.Page
}
if (!string.IsNullOrEmpty(Request["year"]))
qry = qry.OrderByDescending(o => o.activity?.startDate_solar).ThenByDescending(o=>o.up_time).ThenByDescending(o=>o.order_no);
qry = qry.OrderByDescending(o => o.activity != null ? o.activity.startDate_solar : null).ThenByDescending(o=>o.up_time).ThenByDescending(o=>o.order_no);
else
qry = qry.OrderByDescending(o => o.order_no);
@@ -186,8 +186,9 @@ public partial class admin_follower_print_ : System.Web.UI.Page
tdesc = publicFun.enum_desc<Model.pro_order.detailKeyin1>();
//明細
_detail = _db.pro_order_detail.AsEnumerable().Where(x => prod.Select(o => o.order_no).Contains( x.order_no)).ToList();
_bedDt = _db.bed_order_detail.AsEnumerable().Where(x => prod.Select(o => o.order_no).Contains( x.bed_order.order_no)).ToList();
var orderNos = prod.Select(o => o.order_no).ToList();
_detail = _db.pro_order_detail.Where(x => orderNos.Contains(x.order_no)).ToList();
_bedDt = _db.bed_order_detail.Where(x => orderNos.Contains(x.bed_order.order_no)).ToList();
Repeater1.DataSource = prod;
Repeater1.DataBind();

File diff suppressed because it is too large Load Diff

768
web/admin/pivot/README.md Normal file
View File

@@ -0,0 +1,768 @@
# Pivot Module 執行計劃
## 概述
建立多頁籤數據透視查詢模組,參考 transfer 模組的設計架構,使用相同的技術棧與 UI/UX 模式,實現法會報名資料的多維度分析與展示。
---
## 技術架構
### 前端技術
- **框架**: Vue.js 2.x + Vuetify 2.x與 transfer 模組一致)
- **表格元件**: v-data-tableVuetify 內建表格元件)
- **頁籤元件**: v-tabs / v-tab / v-tab-itemVuetify 頁籤)
- **UI 框架**: Bootstrap 5 + Bootstrap IconsMasterPage
- **樣式**: 與 transfer 模組保持一致的視覺風格
### 後端 API
- **框架**: ASP.NET Web APIC#
- **ORM**: Entity Framework + LINQ
- **控制器**: `App_Code/api/pivotController.cs`(已建立)
- **資料庫視圖**: `報名明細查詢`SQL View
### 資料流架構(重要)★
```
查詢流程:
┌─────────────┐
│ Tab 1 │ → 選擇法會 → API 查詢一次 →
│ 查詢條件 │ (完整資料集)
└─────────────┘
┌─────────────────────────────────────────┐
│ Vue Data (存於前端 this.rawData) │
│ - 完整報名明細 │
│ - 一次性載入,不重複查詢 │
└─────────────────────────────────────────┘
┌──────┬──────┬──────┬──────┬──────┐
│Tab 2 │Tab 3 │Tab 4 │Tab 5 │Tab 6 │
│明細 │信眾 │收入 │趨勢 │對比 │
│ │ │ │ │ │
│ 純前端計算 / 過濾 / 分組 / 統計 │
│ 使用 computed / methods / filters │
└──────────────────────────────────────┘
```
**優點**:
1.**效能優化**: API 只查詢一次,減少伺服器負載
2.**即時響應**: 切換頁籤無延遲,使用者體驗佳
3.**離線分析**: 資料載入後可離線操作(過濾、排序、統計)
4.**減少流量**: 不重複傳輸相同資料
5.**一致性**: 所有頁籤基於同一份資料,確保一致性
**技術實現**:
- `this.rawData`: 原始完整資料Tab 1 查詢後存入)
- `computed properties`: 各頁籤的資料來源(動態計算)
- `methods`: 過濾、分組、統計邏輯
- `watch`: 監聽過濾條件變化
### 元件規格
1. **日期選擇器**: `v-date-picker``<input type="date">`
2. **下拉選單**: `v-select`(年份、月份、法會選擇)
3. **資料表格**: `v-data-table`(分頁、排序、過濾)
4. **頁籤切換**: `v-tabs`Tab 1~N
5. **按鈕群組**: `v-btn`(查詢、匯出、重設)
6. **載入狀態**: `:loading="loading"`
7. **視覺標記**: Bootstrap Badge橙、藍、綠、紫色標籤
---
## 頁籤設計
### Tab 1: 查詢條件設定
**功能目標**: 提供查詢條件,篩選法會並選擇目標法會
#### UI 布局
```
+--------------------------------------------------------------+
| [查詢條件] |
|--------------------------------------------------------------|
| 時間範圍: [年份 ▼] [月份 ▼] [查詢法會] |
|--------------------------------------------------------------|
| 法會清單: |
| +----------------------------------------------------------+ |
| | 序號 | 法會名稱 | 開始日期 | 結束日期 | 報名人數 | 操作 | |
| +----------------------------------------------------------+ |
| | 1 | 2025春季法會 | 2025-03-01 | 2025-03-15 | 120 | [選擇] | |
| | 2 | 2025夏季法會 | 2025-06-01 | 2025-06-20 | 85 | [選擇] | |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+
```
#### 查詢流程
1. 選擇年份2020~2025或月份1~12
2. 點擊「查詢法會」按鈕
3. API 回傳該期間的法會清單(`api/pivot/activity_stats`
4. 表格呈現法會清單(法會名稱、日期、統計)
5. 點擊「選擇」按鈕,載入該法會的詳細資料
6. 自動切換到 Tab 2詳細資料頁籤
#### API 整合
- **端點**: `GET api/pivot/activity_stats?startDate={start}&endDate={end}`
- **回傳**: 法會清單(含報名統計)
#### 資料查詢策略
```javascript
// 選擇法會後,一次性載入完整資料
selectActivity(item) {
this.loading = true;
this.selectedActivity = item;
// 一次性查詢完整報名明細(不分頁)
axios.get('/api/pivot/registration_details', {
params: {
activityNum: item.法會ID,
pageSize: 9999 // 取得全部資料
}
}).then(response => {
// 存入原始資料(供所有頁籤使用)
this.rawData = response.data.data.list;
// 自動切換到 Tab 2
this.activeTab = 1;
this.loading = false;
});
}
```
#### 元件範例
```html
<v-select
:items="yearOptions"
v-model="selectedYear"
label="年份"
dense
outlined
></v-select>
<v-select
:items="monthOptions"
v-model="selectedMonth"
label="月份"
dense
outlined
clearable
></v-select>
<v-btn color="primary" @click="loadActivities">查詢法會</v-btn>
<v-data-table
:headers="activityHeaders"
:items="activities"
:loading="loading"
item-key="法會ID"
>
<template v-slot:item.actions="{ item }">
<v-btn small color="success" @click="selectActivity(item)">選擇</v-btn>
</template>
</v-data-table>
```
---
### Tab 2: 報名明細資料
**功能目標**: 完整呈現該場法會的所有報名明細
#### UI 布局
```
+--------------------------------------------------------------+
| [報名明細] 法會: 2025春季法會 (2025-03-01 ~ 2025-03-15) |
|--------------------------------------------------------------|
| 過濾: [信眾姓名] [功德類型 ▼] [狀態 ▼] [查詢] [匯出Excel] |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 報名編號 | 報名日期 | 信眾姓名 | 功德名稱 | 數量 | 金額 | |
| +----------------------------------------------------------+ |
| | 20250301001 | 2025-03-01 | 張三 | 點燈 | 1 | 500 | |
| | 20250301002 | 2025-03-01 | 李四 | 安太歲 | 1 | 300 | |
| +----------------------------------------------------------+ |
| 第 1 頁,共 10 頁(共 200 筆) |
+--------------------------------------------------------------+
```
#### 功能特性
1. **欄位顯示**: 報名編號、報名日期、信眾姓名、功德名稱、數量、金額、已收、未收
2. **過濾條件**: 信眾姓名(模糊搜尋)、功德類型(下拉選單)、功德主(是/否)
3. **排序**: 可依任意欄位排序(升序/降序)
4. **分頁**: 預設每頁 50 筆可調整10/20/50/100
5. **匯出**: 匯出 Excel/CSV
#### 欄位色彩標記(參考 Excel 視圖)
- **橙色(法會資料)**: 法會ID、法會名稱、開始日期、結束日期
- **藍色(信眾資料)**: 信眾編號、信眾姓名
- **綠色(功德資訊)**: 報名編號、報名日期、功德主、功德類型、功德名稱
- **紫色(計算欄位)**: 數量、金額、已收、未收
使用 Bootstrap Badge 或背景色區隔:
```html
<span class="badge bg-warning text-dark">法會</span>
<span class="badge bg-info">信眾</span>
<span class="badge bg-success">功德</span>
<span class="badge bg-secondary">計算</span>
```
#### 資料來源(前端計算)
```javascript
computed: {
// Tab 2: 報名明細(前端分頁、過濾)
filteredRegistrations() {
let data = this.rawData;
// 過濾:信眾姓名
if (this.filter.followerName) {
data = data.filter(x => x.信眾姓名.includes(this.filter.followerName));
}
// 過濾:功德類型
if (this.filter.itemKind) {
data = data.filter(x => x.功德類型 === this.filter.itemKind);
}
// 過濾:功德主
if (this.filter.isParent !== null) {
data = data.filter(x => x.功德主 === (this.filter.isParent ? '是' : '否'));
}
return data;
},
// 前端分頁
paginatedRegistrations() {
const start = (this.currentPage - 1) * this.pageSize;
const end = start + this.pageSize;
return this.filteredRegistrations.slice(start, end);
}
}
```
#### 元件範例
```html
<v-data-table
:headers="detailHeaders"
:items="registrations"
:loading="loading"
:server-items-length="totalCount"
:options.sync="options"
item-key="報名編號"
class="elevation-1"
>
<template v-slot:item.信眾姓名="{ item }">
<span class="badge bg-info me-1"></span>{{ item.信眾姓名 }}
</template>
<template v-slot:item.金額="{ item }">
<span class="badge bg-secondary me-1"></span>{{ item.金額 | currency }}
</template>
</v-data-table>
```
---
### Tab 3: 信眾參與分析
**功能目標**: 統計信眾的參與情況(參與次數、金額、最近參與)
#### UI 布局
```
+--------------------------------------------------------------+
| [信眾參與分析] |
|--------------------------------------------------------------|
| 過濾: [信眾編號] [參與次數 ≥] [總金額 ≥] [查詢] |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 信眾編號 | 姓名 | 參與次數 | 總金額 | 最近參與日期 | |
| +----------------------------------------------------------+ |
| | F001 | 張三 | 5 | 2500 | 2025-03-01 | |
| | F002 | 李四 | 3 | 1500 | 2025-02-15 | |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+
```
#### 功能特性
1. **統計欄位**: 參與次數、總金額、平均金額、最近參與日期
2. **過濾條件**: 信眾編號、參與次數閾值、總金額閾值
3. **排序**: 預設依參與次數降序
4. **分頁**: 預設每頁 50 筆
#### 資料來源(前端計算)
```javascript
computed: {
// Tab 3: 信眾參與分析(從 rawData 計算)
followerAnalysis() {
const followerMap = {};
this.rawData.forEach(item => {
const fNum = item.信眾編號;
if (!followerMap[fNum]) {
followerMap[fNum] = {
信眾編號: fNum,
姓名: item.信眾姓名,
參與次數: 0,
總金額: 0,
最近參與日期: item.報名日期
};
}
followerMap[fNum].參與次數++;
followerMap[fNum].總金額 += (item.金額 * item.數量);
// 更新最近參與日期
if (new Date(item.報名日期) > new Date(followerMap[fNum].最近參與日期)) {
followerMap[fNum].最近參與日期 = item.報名日期;
}
});
// 轉換為陣列,並計算平均金額
return Object.values(followerMap).map(f => ({
...f,
平均金額: Math.round(f.總金額 / f.參與次數)
})).sort((a, b) => b.參與次數 - a.參與次數); // 依參與次數降序
}
}
```
---
### Tab 4: 收入統計分析
**功能目標**: 依時間、法會、功德類型統計收入
#### UI 布局
```
+--------------------------------------------------------------+
| [收入統計分析] |
|--------------------------------------------------------------|
| 分組方式: ( ) 月份 ( ) 年度 (•) 法會 ( ) 功德類型 |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 分組名稱 | 報名人數 | 總金額 | 已收 | 未收 | 收款率 | |
| +----------------------------------------------------------+ |
| | 2025春季法會 | 120 | 60000 | 50000 | 10000 | 83.3% | |
| | 2025夏季法會 | 85 | 45000 | 40000 | 5000 | 88.9% | |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+
```
#### 功能特性
1. **分組方式**: 月份、年度、法會、功德類型
2. **統計欄位**: 報名人數、總金額、已收、未收、收款率
3. **圖表呈現**: 可加入 CanvasJS 長條圖/圓餅圖(選配)
4. **匯出**: 支援 Excel/CSV
#### 資料來源(前端計算)
```javascript
computed: {
// Tab 4: 收入統計分析(從 rawData 計算)
incomeStats() {
const statsMap = {};
const groupBy = this.groupBy; // 'monthly', 'yearly', 'activity', 'itemKind'
this.rawData.forEach(item => {
let key;
switch(groupBy) {
case 'monthly':
key = item.報名日期.substring(0, 7); // YYYY-MM
break;
case 'yearly':
key = item.報名日期.substring(0, 4); // YYYY
break;
case 'activity':
key = item.法會名稱;
break;
case 'itemKind':
key = item.功德類型;
break;
}
if (!statsMap[key]) {
statsMap[key] = {
分組名稱: key,
報名人數: 0,
總金額: 0,
已收: 0,
未收: 0
};
}
statsMap[key].報名人數++;
const amount = item.金額 * item.數量;
statsMap[key].總金額 += amount;
statsMap[key].已收 += item.已收 || 0;
statsMap[key].未收 += item.未收 || amount;
});
// 計算收款率
return Object.values(statsMap).map(s => ({
...s,
收款率: s.總金額 > 0 ? ((s.已收 / s.總金額) * 100).toFixed(1) + '%' : '0%'
}));
}
}
```
---
### Tab 5: 趨勢分析
**功能目標**: 顯示時間序列趨勢(收入、參與人數、法會數量)
#### UI 布局
```
+--------------------------------------------------------------+
| [趨勢分析] |
|--------------------------------------------------------------|
| 指標: ( ) 收入 (•) 參與人數 ( ) 法會數量 |
| 時間間隔: ( ) 月份 (•) 季度 ( ) 年度 |
|--------------------------------------------------------------|
| [折線圖] |
| |
| +----------------------------------------------------------+ |
| | 時間 | 數值 | 成長率 | |
| +----------------------------------------------------------+ |
| | 2025-01 | 120 | +10% | |
| | 2025-02 | 132 | +10% | |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+
```
#### 功能特性
1. **指標選擇**: 收入、參與人數、法會數量
2. **時間間隔**: 月份、季度、年度
3. **成長率計算**: 較前期成長率
4. **圖表**: CanvasJS 折線圖(選配)
#### 資料來源(前端計算)
```javascript
computed: {
// Tab 5: 趨勢分析(從 rawData 計算)
trendAnalysis() {
const metric = this.trendMetric; // 'income', 'followers', 'count'
const interval = this.trendInterval; // 'monthly', 'quarterly', 'yearly'
const trendMap = {};
this.rawData.forEach(item => {
let key;
switch(interval) {
case 'monthly':
key = item.報名日期.substring(0, 7); // YYYY-MM
break;
case 'quarterly':
const month = parseInt(item.報名日期.substring(5, 7));
const quarter = Math.ceil(month / 3);
key = `${item.報名日期.substring(0, 4)}-Q${quarter}`;
break;
case 'yearly':
key = item.報名日期.substring(0, 4); // YYYY
break;
}
if (!trendMap[key]) {
trendMap[key] = { 時間: key, 收入: 0, 人數: 0, 次數: 0 };
}
trendMap[key].收入 += (item.金額 * item.數量);
trendMap[key].人數++; // 簡化計算,實際可用 Set 去重
trendMap[key].次數++;
});
// 轉換為陣列並排序
const result = Object.values(trendMap).sort((a, b) => a.時間.localeCompare(b.時間));
// 計算成長率
return result.map((item, index) => {
if (index === 0) {
return { ...item, 數值: item[metric === 'income' ? '收入' : metric === 'followers' ? '人數' : '次數'], 成長率: '-' };
}
const prev = result[index - 1];
const currentValue = item[metric === 'income' ? '收入' : metric === 'followers' ? '人數' : '次數'];
const prevValue = prev[metric === 'income' ? '收入' : metric === 'followers' ? '人數' : '次數'];
const growthRate = prevValue > 0 ? (((currentValue - prevValue) / prevValue) * 100).toFixed(1) : '0';
return { ...item, 數值: currentValue, 成長率: growthRate + '%' };
});
}
}
```
---
### Tab 6: 對比分析(選配)
**功能目標**: 不同時期、法會的對比分析
#### UI 布局
```
+--------------------------------------------------------------+
| [對比分析] |
|--------------------------------------------------------------|
| 對比類型: (•) 法會對比 ( ) 年度對比 |
| 期間1: [2025春季法會 ▼] 期間2: [2024春季法會 ▼] |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 指標 | 期間1 | 期間2 | 差異 | 差異率 | |
| +----------------------------------------------------------+ |
| | 報名人數 | 120 | 100 | +20 | +20% | |
| | 總金額 | 60000 | 50000 | +10000 | +20% | |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+
```
#### 資料來源(前端計算)
```javascript
computed: {
// Tab 6: 對比分析(從 rawData 計算)
// 注意:對比分析可能需要跨法會資料,如需要可額外查詢
comparativeAnalysis() {
// 如果只在單一法會內對比(如月份對比),可用 rawData
// 如果需要跨法會對比,建議另外查詢或在 Tab 1 時一併載入多場法會資料
const period1Data = this.rawData.filter(x => {
// 依 period1 條件過濾
return this.isPeriod1(x);
});
const period2Data = this.rawData.filter(x => {
// 依 period2 條件過濾
return this.isPeriod2(x);
});
const stats1 = this.calculateStats(period1Data);
const stats2 = this.calculateStats(period2Data);
return [
{ 指標: '報名人數', 期間1: stats1.count, 期間2: stats2.count, 差異: stats1.count - stats2.count },
{ 指標: '總金額', 期間1: stats1.amount, 期間2: stats2.amount, 差異: stats1.amount - stats2.amount },
// ... 其他指標
];
}
}
```
---
## 視覺設計規範
### 色彩系統(參考 transfer
- **主色調**: Bootstrap 5 預設配色
- **成功/確認**: `bg-success` / `text-success`
- **警告**: `bg-warning` / `text-warning`
- **危險/錯誤**: `bg-danger` / `text-danger`
- **資訊**: `bg-info` / `text-info`
- **次要**: `bg-secondary` / `text-muted`
### 欄位色彩標記(對應 Excel
```html
<!-- 橙色:法會資料 -->
<span class="badge bg-warning text-dark"></span>
<!-- 藍色:信眾資料 -->
<span class="badge bg-info"></span>
<!-- 綠色:功德資訊 -->
<span class="badge bg-success">功德</span>
<!-- 紫色:計算欄位 -->
<span class="badge bg-secondary"></span>
```
### 表格樣式
```css
/* 與 transfer 保持一致 */
.v-data-table th {
background-color: #f5f5f5 !important;
font-weight: bold;
}
.v-data-table tbody tr:hover {
background-color: #f0f8ff !important;
}
/* 欄位色彩標記 */
.field-activity { border-left: 3px solid #ffc107; } /* 橙 */
.field-follower { border-left: 3px solid #17a2b8; } /* 藍 */
.field-merit { border-left: 3px solid #28a745; } /* 綠 */
.field-calculated { border-left: 3px solid #6c757d; } /* 紫 */
```
---
## API 端點總覽
| 端點 | 方法 | 功能 | 對應頁籤 | 使用時機 |
|------|------|------|----------|----------|
| `/api/pivot/activity_stats` | GET | 查詢法會清單與統計 | Tab 1 | 選擇查詢條件時 |
| `/api/pivot/registration_details` | GET | 報名明細查詢(**完整資料** | Tab 1 | **選擇法會後一次性載入** ★ |
| `/api/pivot/registration_details_export` | GET | 報名明細匯出 | Tab 2 | 匯出 Excel 時(選配) |
| `/api/pivot/excel_data_structured` | GET | Excel 數據連接 | 外部 | Power Query/Power Pivot |
**重要說明**:
-**Tab 2~6 不呼叫 API**,全部使用前端 `computed` 計算
-`registration_details` 查詢時使用 `pageSize=9999` 取得完整資料
- ✅ 原 `follower_analysis`, `income_stats`, `trend_analysis`, `comparative_analysis` 端點**保留備用**,但前端優先使用 computed
- ✅ 如資料量過大(>5000 筆),可調整策略改用分頁查詢
---
## 檔案結構
```
admin/pivot/
├── index.aspx # 首頁(已完成)
├── index.aspx.cs # 首頁邏輯(已完成)
├── query.aspx # 多頁籤查詢頁面(待建立)★
├── query.aspx.cs # 查詢頁面邏輯(待建立)★
└── README.md # 本文件
App_Code/api/
└── pivotController.cs # API 控制器(已完成)
```
---
## 實作步驟
### Phase 1: 建立查詢頁面骨架
1. ✅ 建立 `query.aspx`(參考 transfer/verify.aspx
2. ✅ 建立 `query.aspx.cs`(參考 transfer/verify.aspx.cs
3. ✅ 引入 Vue.js + Vuetify
4. ✅ 建立頁籤結構v-tabs
### Phase 2: 實作 Tab 1查詢條件
1. ✅ 年份/月份選擇器
2. ✅ 查詢法會按鈕與 API 整合(`activity_stats`
3. ✅ 法會清單表格v-data-table
4. ✅ 選擇法會邏輯(一次性載入完整資料)
5.`this.rawData` 資料結構設計
### Phase 3: 實作 Tab 2報名明細
1. ✅ 報名明細表格(前端分頁、排序、過濾)
2.`computed: filteredRegistrations` 實作
3. ✅ 欄位色彩標記badge
4. ✅ 匯出功能(選配)
### Phase 4: 實作 Tab 3~6分析頁籤
1. ✅ 信眾參與分析Tab 3- `computed: followerAnalysis`
2. ✅ 收入統計分析Tab 4- `computed: incomeStats`
3. ✅ 趨勢分析Tab 5- `computed: trendAnalysis`
4. ✅ 對比分析Tab 6選配- `computed: comparativeAnalysis`
### Phase 5: 優化與測試
1. ✅ UI/UX 調整(與 transfer 保持一致)
2. ✅ 效能優化(分頁、快取)
3. ✅ 瀏覽器相容性測試
4. ✅ 權限控制ezAuthorize
---
## 參考範例
### 1. transfer/verify.aspx
- 多階段流程設計程序1、程序2
- v-data-table 表格元件使用
- 信眾選擇對話框v-dialog
- 狀態選擇v-select
### 2. transfer/verify1.aspx
- 單階段流程設計
- 簡潔的查詢與確認流程
### 3. transfer/index.aspx
- 功能入口頁面設計
- Bootstrap Icons + Badge
- 三欄式布局
---
## 注意事項
1. **元件一致性**: 所有元件、樣式、命名均與 transfer 模組保持一致
2. **API 規範**: 遵循 RESTful 設計,統一回傳格式
3. **權限控制**: 所有 API 加上 `[ezAuthorize]`
4. **效能考量**:
-**前端計算策略**: Tab 1 查詢一次Tab 2~6 使用 Vue computed 計算
-**資料量控制**: 單一法會報名明細預估 <5000 筆,適合前端處理
- ⚠️ **大資料處理**: 若單場法會 >5000 筆,可改用 API 分頁查詢
-**記憶體管理**: 切換法會時清除舊資料(`this.rawData = []`
5. **視覺區隔**: 使用 Badge、邊框色、背景色標記欄位類型
6. **使用者體驗**: 載入狀態loading、錯誤提示alert、空資料提示
7. **前端效能優化**:
- 使用 `computed` 而非 `methods`(自動快取)
- 大型陣列操作使用 `Object.freeze()` 凍結原始資料
- v-data-table 啟用虛擬滾動(`:virtual-scroll="true"`,資料量 >1000 時)
---
## SQL View 參考
```SQL
CREATE VIEW [dbo].[]
AS
SELECT dbo.activity.num AS ID, dbo.activity.subject AS , dbo.activity.startDate_solar AS , dbo.activity.endDate_solar AS ,
dbo.followers.f_number AS , dbo.followers.u_name AS , dbo.pro_order.order_no AS , dbo.pro_order.up_time AS ,
CASE WHEN parent_num IS NOT NULL THEN '' ELSE '' END AS , dbo.actItem_kind.kind AS , dbo.actItem.subject AS ,
dbo.pro_order_detail.qty AS , dbo.pro_order_detail.price AS , 0 AS , dbo.pro_order_detail.price * dbo.pro_order_detail.qty - 0 AS
FROM dbo.pro_order_detail INNER JOIN
dbo.pro_order ON dbo.pro_order_detail.order_no = dbo.pro_order.order_no INNER JOIN
dbo.actItem ON dbo.pro_order_detail.actItem_num = dbo.actItem.num INNER JOIN
dbo.activity ON dbo.pro_order.activity_num = dbo.activity.num INNER JOIN
dbo.followers ON dbo.pro_order.f_num = dbo.followers.num INNER JOIN
dbo.actItem_kind ON dbo.actItem.kind = dbo.actItem_kind.num
GO
```
---
## 資料結構範例
```javascript
// Vue data 結構
data() {
return {
// 原始完整資料Tab 1 查詢後存入,供所有頁籤使用)
rawData: [], // Array<報名明細物件>
// 選中的法會
selectedActivity: null,
// 當前頁籤索引
activeTab: 0, // 0=Tab1, 1=Tab2, 2=Tab3...
// Tab 2 過濾條件
filter: {
followerName: '',
itemKind: null,
isParent: null
},
// Tab 4 分組方式
groupBy: 'activity', // 'monthly', 'yearly', 'activity', 'itemKind'
// Tab 5 趨勢設定
trendMetric: 'income', // 'income', 'followers', 'count'
trendInterval: 'monthly', // 'monthly', 'quarterly', 'yearly'
// 載入狀態
loading: false
}
}
```
## Vue Computed 效能考量
```javascript
computed: {
// 使用 Object.freeze() 凍結大型陣列,提升效能
frozenRawData() {
return Object.freeze(this.rawData);
},
// 各頁籤 computed 基於 frozenRawData
filteredRegistrations() {
return this.frozenRawData.filter(/* 過濾邏輯 */);
}
}
```
## 執行確認
請檢視以上計劃,確認以下事項:
1. ✅ 頁籤設計Tab 1~6符合需求
2. ✅ 查詢流程(年/月 → 法會清單 → 明細資料)清晰
3. ✅ 視覺區隔色彩標記、Badge符合 Excel 概念
4. ✅ 技術架構Vue + Vuetify + API與 transfer 一致
5. ✅ 功能完整性(查詢、過濾、排序、匯出)
6. ✅ **資料流架構(一次查詢 + 前端計算)**合理且高效 ★
**確認後即開始實作 Phase 1建立查詢頁面骨架。**

185
web/admin/pivot/index.aspx Normal file
View File

@@ -0,0 +1,185 @@
<%@ Page Title="數據透視管理" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" EnableEventValidation="false" CodeFile="index.aspx.cs" Inherits="admin_pivot_index" %>
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
<link rel="stylesheet" href="../../js/_bootstrap-icons-1.8.1/bootstrap-icons.css">
<style>
.function-icon {
font-size: 2em;
line-height: 1;
align-content: center;
}
.external-link-icon {
font-size: 0.8em;
}
</style>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="page_nav" runat="Server">
<h2 class="mb-3">數據透視管理</h2>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<div id="content" class="container py-4">
<div class="row">
<!-- 第一欄:報表查詢 -->
<div class="col-lg-4 mb-4">
<h5 class="text-primary mb-3">
<i class="bi bi-graph-up"></i> 報表查詢
</h5>
<div class="list-group">
<a href="activity_report.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-bar-chart text-success me-3 function-icon"></i>
<div>
<div>法會報名統計</div>
<small class="text-muted">各法會報名人數與金額統計</small>
</div>
</div>
<span class="badge bg-primary">管理員</span>
</div>
</a>
<a href="follower_report.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-people text-info me-3 function-icon"></i>
<div>
<div>信眾參與分析</div>
<small class="text-muted">信眾參與法會的詳細分析</small>
</div>
</div>
<span class="badge bg-info">管理員</span>
</div>
</a>
<a href="income_report.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-currency-dollar text-warning me-3 function-icon"></i>
<div>
<div>收入統計報表</div>
<small class="text-muted">各項功德金收入統計分析</small>
</div>
</div>
<span class="badge bg-warning text-dark">財務</span>
</div>
</a>
</div>
</div>
<!-- 第二欄:數據分析 -->
<div class="col-lg-4 mb-4">
<h5 class="text-primary mb-3">
<i class="bi bi-pie-chart"></i> 數據分析
</h5>
<div class="list-group">
<a href="query.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-diagram-3 text-primary me-3 function-icon"></i>
<div>
<div>數據透視查詢</div>
<small class="text-muted">多維度數據透視分析(完整版)</small>
</div>
</div>
<span class="badge bg-success">NEW</span>
</div>
</a>
<a href="pivot_analysis.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-diagram-3 text-secondary me-3 function-icon"></i>
<div>
<div>樞紐分析(舊版)</div>
<small class="text-muted">多維度數據透視分析</small>
</div>
</div>
<span class="badge bg-secondary">分析師</span>
</div>
</a>
<a href="trend_analysis.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-graph-up-arrow text-success me-3 function-icon"></i>
<div>
<div>趨勢分析</div>
<small class="text-muted">時間序列趨勢變化分析</small>
</div>
</div>
<span class="badge bg-success">分析師</span>
</div>
</a>
<a href="comparative_analysis.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-graph-down text-danger me-3 function-icon"></i>
<div>
<div>對比分析</div>
<small class="text-muted">不同時期、法會的對比分析</small>
</div>
</div>
<span class="badge bg-danger">分析師</span>
</div>
</a>
</div>
</div>
<!-- 第三欄:報表管理 -->
<div class="col-lg-4 mb-4">
<h5 class="text-primary mb-3">
<i class="bi bi-file-earmark-text"></i> 報表管理
</h5>
<div class="list-group">
<a href="custom_report.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-file-plus text-info me-3 function-icon"></i>
<div>
<div>自訂報表</div>
<small class="text-muted">建立自訂的報表範本</small>
</div>
</div>
<span class="badge bg-info">管理員</span>
</div>
</a>
<a href="report_schedule.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-clock text-secondary me-3 function-icon"></i>
<div>
<div>定期報表</div>
<small class="text-muted">設定定期自動產生報表</small>
</div>
</div>
<span class="badge bg-secondary">管理員</span>
</div>
</a>
<a href="export_center.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-download text-success me-3 function-icon"></i>
<div>
<div>匯出中心</div>
<small class="text-muted">報表匯出與下載管理</small>
</div>
</div>
<span class="badge bg-success">使用者</span>
</div>
</a>
</div>
</div>
</div>
<!-- 統計資訊 -->
<div class="row mt-4">
<div class="col-12">
<div class="alert alert-info">
<h6 class="alert-heading">
<i class="bi bi-info-circle"></i> 系統說明
</h6>
<ul class="mb-0">
<li><strong>報表查詢</strong>:提供各種預設報表的查詢與統計功能</li>
<li><strong>數據分析</strong>:多維度的數據透視與趨勢分析工具</li>
<li><strong>報表管理</strong>:自訂報表建立、定期報表設定與匯出管理</li>
</ul>
</div>
</div>
</div>
</div>
</asp:Content>

View File

@@ -0,0 +1,10 @@
using System;
using System.Web.UI;
public partial class admin_pivot_index : MyWeb.config
{
protected void Page_Load(object sender, EventArgs e)
{
// 頁面初始化(暫無邏輯)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
using System;
using System.Web.UI;
public partial class admin_pivot_pivot01 : MyWeb.config
{
protected void Page_Load(object sender, EventArgs e)
{
// 頁面初始化(暫無邏輯)
// 前端直接呼叫 API
}
}

1128
web/admin/pivot/query.aspx Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
using System;
using System.Web.UI;
public partial class admin_pivot_query : MyWeb.config
{
protected void Page_Load(object sender, EventArgs e)
{
// 頁面初始化(暫無邏輯)
}
}

View File

@@ -21,7 +21,7 @@ public partial class admin_project_kind_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.project_kind.AsEnumerable().Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
var prod = _db.project_kind.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -210,7 +210,7 @@ public partial class admin_project_kind_reg : MyWeb.config
try
{
var prod = _db.project_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.project_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -248,7 +248,7 @@ public partial class admin_project_kind_reg : MyWeb.config
int num = Val(Request["num"]);
del_product(num);
var prod = _db.project_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.project_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.project_kind.Remove(prod);
@@ -265,7 +265,7 @@ public partial class admin_project_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.project_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.project_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -283,7 +283,7 @@ public partial class admin_project_kind_reg : MyWeb.config
public void del_product(int num)
{
var prod = _db.projects.AsEnumerable().Where(q => q.kind == num).ToList();
var prod = _db.projects.Where(q => q.kind == num).ToList();
if (prod.Count > 0)
{
////查詢結果全部刪除

View File

@@ -39,9 +39,8 @@ public partial class admin_project_list : MyWeb.config
//品項
s_actItem_num.Items.Clear();
s_actItem_num.Items.Add(new ListItem("請選擇", ""));
var qry = _db.actItems.AsEnumerable();
qry = qry.Where(o => (int?)o.category==(int)Model.activity.category.Patronize);//贊助項目
qry = qry.OrderByDescending(o => o.num).ToList();
var qry = _db.actItems.Where(o => (int?)o.category==(int)Model.activity.category.Patronize);//贊助項目
qry = qry.OrderByDescending(o => o.num);
if(qry.Count() > 0)
foreach(var qq in qry)
s_actItem_num.Items.Add(new ListItem(qq.subject, qq.num.ToString()));
@@ -99,7 +98,7 @@ public partial class admin_project_list : MyWeb.config
sd.AppendChild(tr);
//查詢要匯出的資料
var qry = _db.pro_order_detail.AsEnumerable();
var qry = _db.pro_order_detail.AsQueryable();
qry = qry.Where(o => (int?)o.actItem.category == (int)Model.activity.category.Patronize);
if (!string.IsNullOrEmpty(s_f_num.Value))
@@ -111,7 +110,8 @@ public partial class admin_project_list : MyWeb.config
var list = qry.ToList();
if (list.Count > 0)
{
var projectDt = _db.projects.AsEnumerable(); //專案
// TODO: REVIEW - projects 資料量可能成長,若超過數千筆需優化為按需查詢
var projectDt = _db.projects.ToList(); //專案
foreach (var item in list)
{
var projects = from s in projectDt

View File

@@ -21,7 +21,7 @@ public partial class admin_stock_kind_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.stock_kind.AsEnumerable().Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
var prod = _db.stock_kind.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -210,7 +210,7 @@ public partial class admin_stock_kind_reg : MyWeb.config
try
{
var prod = _db.stock_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.stock_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -248,7 +248,7 @@ public partial class admin_stock_kind_reg : MyWeb.config
int num = Val(Request["num"]);
del_product(num);
var prod = _db.stock_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.stock_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.stock_kind.Remove(prod);
@@ -266,7 +266,7 @@ public partial class admin_stock_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.stock_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.stock_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -285,7 +285,7 @@ public partial class admin_stock_kind_reg : MyWeb.config
public void del_product(int num)
{
var prod = _db.stocks.AsEnumerable().Where(q => q.kind == num).ToList();
var prod = _db.stocks.Where(q => q.kind == num).ToList();
if (prod.Count > 0)
{
////查詢結果全部刪除

View File

@@ -22,7 +22,7 @@ public partial class admin_stock_reason_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.stock_reason.AsEnumerable().Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
var prod = _db.stock_reason.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -221,7 +221,7 @@ public partial class admin_stock_reason_reg : MyWeb.config
try
{
var prod = _db.stock_reason.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.stock_reason.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -257,7 +257,7 @@ public partial class admin_stock_reason_reg : MyWeb.config
{
int num = Val(Request["num"]);
var prod = _db.stock_reason.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.stock_reason.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.stock_reason.Remove(prod);
@@ -275,7 +275,7 @@ public partial class admin_stock_reason_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.stock_reason.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.stock_reason.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)

View File

@@ -21,7 +21,7 @@ public partial class admin_supplier_kind_reg : MyWeb.config
if (!isStrNull(Request["num"]))
{
int _num = Val(Request["num"]);
var prod = _db.supplier_kind.AsEnumerable().Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
var prod = _db.supplier_kind.Where(q => q.num == _num).OrderBy(q => q.kind).FirstOrDefault();
if (prod != null)
{
@@ -210,7 +210,7 @@ public partial class admin_supplier_kind_reg : MyWeb.config
try
{
var prod = _db.supplier_kind.AsEnumerable().Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
var prod = _db.supplier_kind.Where(q => q.root == root).OrderByDescending(q => q.range).FirstOrDefault();
if (prod != null)
if (prod.range.HasValue)
range = prod.range.Value + 1;
@@ -248,7 +248,7 @@ public partial class admin_supplier_kind_reg : MyWeb.config
int num = Val(Request["num"]);
//del_product(num);
var prod = _db.supplier_kind.AsEnumerable().Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
var prod = _db.supplier_kind.Where(q => q.num == num).FirstOrDefault(); //刪除該筆資料
if (prod != null)
{
_db.supplier_kind.Remove(prod);
@@ -266,7 +266,7 @@ public partial class admin_supplier_kind_reg : MyWeb.config
public void Del_Ohter_Items(int d_num)
{
var prod = _db.supplier_kind.AsEnumerable().Where(q => q.root == d_num).ToList();
var prod = _db.supplier_kind.Where(q => q.root == d_num).ToList();
if (prod.Count > 0)
{
foreach (var row in prod)
@@ -285,7 +285,7 @@ public partial class admin_supplier_kind_reg : MyWeb.config
public void del_product(int num)
{
var prod = _db.suppliers.AsEnumerable().Where(q => q.kind == num).ToList();
var prod = _db.suppliers.Where(q => q.kind == num).ToList();
if (prod.Count > 0)
{
////查詢結果全部刪除

View File

@@ -0,0 +1,28 @@
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="test_asenumerable_check.aspx.cs" Inherits="test_asenumerable_check" %>
<!DOCTYPE html>
<html>
<head>
<title>AsEnumerable 修改檢測工具</title>
<style>
body { font-family: 'Consolas', monospace; margin: 20px; background: #1e1e1e; color: #d4d4d4; }
.success { color: #4ec9b0; font-weight: bold; }
.error { color: #f48771; font-weight: bold; }
.warning { color: #dcdcaa; font-weight: bold; }
.sql-box { background: #252526; padding: 15px; margin: 10px 0; border-left: 4px solid #007acc; }
.test-item { margin: 20px 0; padding: 15px; background: #2d2d30; border-radius: 4px; }
h2 { color: #4ec9b0; }
h3 { color: #dcdcaa; margin-top: 20px; }
pre { white-space: pre-wrap; word-wrap: break-word; }
</style>
</head>
<body>
<h1>🔍 AsEnumerable 修改檢測報告</h1>
<form runat="server">
<asp:Button ID="btnRunTest" runat="server" Text="▶ 執行檢測" OnClick="btnRunTest_Click"
style="padding: 10px 20px; font-size: 16px; cursor: pointer;" />
<hr />
<asp:Literal ID="litResult" runat="server"></asp:Literal>
</form>
</body>
</html>

View File

@@ -0,0 +1,346 @@
using System;
using System.Text;
using System.Diagnostics;
using System.Linq;
using Model;
public partial class test_asenumerable_check : System.Web.UI.Page
{
private StringBuilder output = new StringBuilder();
private int passCount = 0;
private int failCount = 0;
private int warnCount = 0;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
litResult.Text = "<p style='color:#888;'>點擊「執行檢測」開始測試修改結果</p>";
}
}
protected void btnRunTest_Click(object sender, EventArgs e)
{
output.Clear();
passCount = 0;
failCount = 0;
warnCount = 0;
output.AppendLine("<div style='margin-bottom: 20px;'>");
output.AppendLine($"<p>測試時間: {DateTime.Now:yyyy-MM-dd HH:mm:ss}</p>");
output.AppendLine("</div>");
// 執行所有測試
TestAccountingKind();
TestActivityKind();
TestFollowers();
TestMembers();
TestOrders();
// 顯示總結
output.AppendLine("<hr />");
output.AppendLine("<h2>📊 測試總結</h2>");
output.AppendLine($"<p class='success'>✅ 通過: {passCount} 項</p>");
output.AppendLine($"<p class='error'>❌ 失敗: {failCount} 項</p>");
output.AppendLine($"<p class='warning'>⚠️ 警告: {warnCount} 項</p>");
if (failCount == 0)
{
output.AppendLine("<h3 class='success'>🎉 所有測試通過!修改成功!</h3>");
}
else
{
output.AppendLine("<h3 class='error'>⚠️ 發現問題,請檢查失敗的項目</h3>");
}
litResult.Text = output.ToString();
}
private void TestAccountingKind()
{
output.AppendLine("<h3>測試 1: accounting_kind 查詢優化</h3>");
using (var db = new ezEntities())
{
var sqlLog = new StringBuilder();
db.Database.Log = sql => sqlLog.AppendLine(sql);
try
{
// 測試 1.1: 單筆查詢
sqlLog.Clear();
var sw = Stopwatch.StartNew();
var result = db.accounting_kind.Where(q => q.num == 1).FirstOrDefault();
sw.Stop();
CheckQuery("單筆查詢 (Where + FirstOrDefault)",
sqlLog.ToString(),
sw.ElapsedMilliseconds,
shouldHaveWhere: true,
shouldHaveSelectAll: false,
maxTime: 50);
// 測試 1.2: 檢查子資料 (應該用 Any)
sqlLog.Clear();
sw.Restart();
var hasChildren = db.accounting_kind.Any(q => q.root == 1);
sw.Stop();
CheckQuery("檢查子資料存在 (Any)",
sqlLog.ToString(),
sw.ElapsedMilliseconds,
shouldHaveWhere: true,
shouldHaveExists: true,
maxTime: 30);
// 測試 1.3: 取得子資料清單
sqlLog.Clear();
sw.Restart();
var children = db.accounting_kind.Where(q => q.root == 1).ToList();
sw.Stop();
CheckQuery("取得子資料清單 (Where + ToList)",
sqlLog.ToString(),
sw.ElapsedMilliseconds,
shouldHaveWhere: true,
shouldHaveSelectAll: false,
maxTime: 100);
}
catch (Exception ex)
{
LogError($"accounting_kind 測試失敗: {ex.Message}");
}
}
}
private void TestActivityKind()
{
output.AppendLine("<h3>測試 2: activity_kind 查詢優化</h3>");
using (var db = new ezEntities())
{
var sqlLog = new StringBuilder();
db.Database.Log = sql => sqlLog.AppendLine(sql);
try
{
// 單筆查詢 + 排序
sqlLog.Clear();
var sw = Stopwatch.StartNew();
var result = db.activity_kind
.Where(q => q.num == 1)
.OrderBy(q => q.kind)
.FirstOrDefault();
sw.Stop();
CheckQuery("單筆查詢 + 排序 (Where + OrderBy + FirstOrDefault)",
sqlLog.ToString(),
sw.ElapsedMilliseconds,
shouldHaveWhere: true,
shouldHaveOrderBy: true,
maxTime: 50);
}
catch (Exception ex)
{
LogError($"activity_kind 測試失敗: {ex.Message}");
}
}
}
private void TestFollowers()
{
output.AppendLine("<h3>測試 3: followers 查詢優化</h3>");
using (var db = new ezEntities())
{
var sqlLog = new StringBuilder();
db.Database.Log = sql => sqlLog.AppendLine(sql);
try
{
// 測試字串查詢 (Contains)
sqlLog.Clear();
var sw = Stopwatch.StartNew();
var result = db.followers
.Where(q => q.f_number.Contains("A"))
.Take(10)
.ToList();
sw.Stop();
CheckQuery("字串查詢 (Contains)",
sqlLog.ToString(),
sw.ElapsedMilliseconds,
shouldHaveWhere: true,
shouldHaveLike: true,
maxTime: 100);
}
catch (Exception ex)
{
LogError($"followers 測試失敗: {ex.Message}");
}
}
}
private void TestMembers()
{
output.AppendLine("<h3>測試 4: members 查詢優化</h3>");
using (var db = new ezEntities())
{
var sqlLog = new StringBuilder();
db.Database.Log = sql => sqlLog.AppendLine(sql);
try
{
// 測試批次查詢
var ids = new[] { 1, 2, 3 };
sqlLog.Clear();
var sw = Stopwatch.StartNew();
var result = db.members.Where(q => ids.Contains(q.num)).ToList();
sw.Stop();
CheckQuery("批次 ID 查詢 (Contains)",
sqlLog.ToString(),
sw.ElapsedMilliseconds,
shouldHaveWhere: true,
shouldHaveIn: true,
maxTime: 100);
}
catch (Exception ex)
{
LogError($"members 測試失敗: {ex.Message}");
}
}
}
private void TestOrders()
{
output.AppendLine("<h3>測試 5: pro_order_detail 聚合查詢</h3>");
using (var db = new ezEntities())
{
var sqlLog = new StringBuilder();
db.Database.Log = sql => sqlLog.AppendLine(sql);
try
{
// 測試 Count
sqlLog.Clear();
var sw = Stopwatch.StartNew();
var count = db.pro_order_detail.Count(q => q.num > 0);
sw.Stop();
CheckQuery("Count 查詢",
sqlLog.ToString(),
sw.ElapsedMilliseconds,
shouldHaveWhere: true,
shouldHaveCount: true,
maxTime: 50);
}
catch (Exception ex)
{
LogError($"pro_order_detail 測試失敗: {ex.Message}");
}
}
}
private void CheckQuery(string testName, string sql, long milliseconds,
bool shouldHaveWhere = false,
bool shouldHaveSelectAll = false,
bool shouldHaveExists = false,
bool shouldHaveOrderBy = false,
bool shouldHaveLike = false,
bool shouldHaveIn = false,
bool shouldHaveCount = false,
long maxTime = 100)
{
output.AppendLine("<div class='test-item'>");
output.AppendLine($"<h4>🔸 {testName}</h4>");
output.AppendLine($"<p>執行時間: {milliseconds}ms</p>");
bool passed = true;
var issues = new StringBuilder();
// 檢查是否有 WHERE 條件
if (shouldHaveWhere && !sql.Contains("WHERE"))
{
issues.AppendLine("❌ 缺少 WHERE 條件(可能載入整表)<br/>");
passed = false;
}
// 檢查是否有不當的 SELECT *
if (!shouldHaveSelectAll && sql.Contains("SELECT") && !sql.Contains("WHERE") && !sql.Contains("TOP"))
{
issues.AppendLine("❌ SELECT 沒有條件限制(載入整表)<br/>");
passed = false;
}
// 檢查是否使用 EXISTSAny 應該產生)
if (shouldHaveExists && !sql.Contains("EXISTS"))
{
issues.AppendLine("⚠️ 建議使用 EXISTS 而非載入資料<br/>");
warnCount++;
}
// 檢查是否有 ORDER BY
if (shouldHaveOrderBy && !sql.Contains("ORDER BY"))
{
issues.AppendLine("❌ 缺少 ORDER BY排序應該在資料庫執行<br/>");
passed = false;
}
// 檢查是否有 LIKEContains 應該產生)
if (shouldHaveLike && !sql.Contains("LIKE"))
{
issues.AppendLine("❌ 字串查詢未轉為 LIKE<br/>");
passed = false;
}
// 檢查是否有 IN批次查詢應該產生
if (shouldHaveIn && !sql.Contains("IN"))
{
issues.AppendLine("❌ 批次查詢未轉為 IN<br/>");
passed = false;
}
// 檢查是否有 COUNT聚合函數應該在資料庫執行
if (shouldHaveCount && !sql.Contains("COUNT"))
{
issues.AppendLine("❌ COUNT 未在資料庫執行<br/>");
passed = false;
}
// 檢查執行時間
if (milliseconds > maxTime)
{
issues.AppendLine($"⚠️ 執行時間較長 (>{maxTime}ms),可能仍有優化空間<br/>");
warnCount++;
}
if (passed)
{
output.AppendLine("<p class='success'>✅ 測試通過</p>");
passCount++;
}
else
{
output.AppendLine("<p class='error'>❌ 測試失敗</p>");
output.AppendLine($"<div style='color:#f48771;'>{issues}</div>");
failCount++;
}
// 顯示 SQL
output.AppendLine("<div class='sql-box'>");
output.AppendLine("<strong>產生的 SQL:</strong><br/>");
output.AppendLine($"<pre>{System.Web.HttpUtility.HtmlEncode(sql)}</pre>");
output.AppendLine("</div>");
output.AppendLine("</div>");
}
private void LogError(string message)
{
output.AppendLine($"<p class='error'>❌ {message}</p>");
failCount++;
}
}