Đề tài Tìm hiểu và xây dựng ứng dụng web siêu thị trực tuyến với ASP.net MVC

Tài liệu Đề tài Tìm hiểu và xây dựng ứng dụng web siêu thị trực tuyến với ASP.net MVC: ĐẠI HỌC THĂNG LONG BỘ MÔN TIN HỌC CHUYÊN ĐỀ TỐT NGHIỆP TÌM HIỂU VÀ XÂY DỰNG ỨNG DỤNG WEB SIÊU THỊ TRỰC TUYẾN VỚI ASP.NET MVC SINH VIÊN : ĐẶNG TIẾN LỘC – A07138 GIÁO VIÊN HƯỚNG DẪN : Ths. ĐOÀN QUANG MINH HÀ NỘI 5/ 2010 BẢNG KÝ HIỆU VIẾT TẮT MVC Model View Controller Mô hình lập trình ba lớp Model, View, Controller được sử dụng trong nhiều framework lập trình web trong đó có ASP.NET MVC, ZEND , J2EE… API Application Programming Interface Giao diện lập trình được sử dụng bởi một ứng dụng qua đó cho phép nó tương tác với ứng dụng khác ACK ACKnowledgement Thừa nhận một trường xác thực trong gói tin http. URL Uniform Resource Locator Địa chỉ website LINQ .NET Language Intergrated Query Ngôn ngữ truy vấn tích hợp với .NET CDN Content Delivery Network Mạng phân phối nội dung SMTP Simple Mail Transfer Protocol Giao thức gửi thư điện tử SSL Secure Socket Layer Tầng bảo mật Socket IIS Internet Information Server Máy chủ thông tin mạng EML Electronic Mail...

doc126 trang | Chia sẻ: hunglv | Lượt xem: 1994 | Lượt tải: 0download
Bạn đang xem trước 20 trang mẫu tài liệu Đề tài Tìm hiểu và xây dựng ứng dụng web siêu thị trực tuyến với ASP.net MVC, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
ĐẠI HỌC THĂNG LONG BỘ MÔN TIN HỌC CHUYÊN ĐỀ TỐT NGHIỆP TÌM HIỂU VÀ XÂY DỰNG ỨNG DỤNG WEB SIÊU THỊ TRỰC TUYẾN VỚI ASP.NET MVC SINH VIÊN : ĐẶNG TIẾN LỘC – A07138 GIÁO VIÊN HƯỚNG DẪN : Ths. ĐOÀN QUANG MINH HÀ NỘI 5/ 2010 BẢNG KÝ HIỆU VIẾT TẮT MVC Model View Controller Mô hình lập trình ba lớp Model, View, Controller được sử dụng trong nhiều framework lập trình web trong đó có ASP.NET MVC, ZEND , J2EE… API Application Programming Interface Giao diện lập trình được sử dụng bởi một ứng dụng qua đó cho phép nó tương tác với ứng dụng khác ACK ACKnowledgement Thừa nhận một trường xác thực trong gói tin http. URL Uniform Resource Locator Địa chỉ website LINQ .NET Language Intergrated Query Ngôn ngữ truy vấn tích hợp với .NET CDN Content Delivery Network Mạng phân phối nội dung SMTP Simple Mail Transfer Protocol Giao thức gửi thư điện tử SSL Secure Socket Layer Tầng bảo mật Socket IIS Internet Information Server Máy chủ thông tin mạng EML Electronic Mail Thư điện tử (đuôi của tệp thư điện tử) DOM Document Object Model định nghĩa tập các đối tượng chuẩn cho tất cả tài liệu có cấu trúc UI User Interface Giao diện người dùng CPU Control Processing Unit Bộ điều khiển xử lí trong máy tính MỤC LỤC GIỚI THIỆU CHUNG Trong chuyên đề tốt nghiệp này em thực hiện việc tìm hiểu ASP.NET MVC , mô hình thương mại điện tử trực tuyến của các chuỗi siêu thị lớn trên thế giới như , qua đó xây dựng hệ thống thương mại trực tuyến trên nền tảng gồm các module: Module thành viên và hồ sơ. Đăng kí tài khoản Đăng nhập, đăng xuất Quản lí hồ sơ Quản lí vai trò người dùng, xóa , tạo vai trò Quản lí người dùng, tìm kiếm người dùng theo tên – theo email , xóa người dùng, sửa đổi thông tin người dùng Moudle thương mại Duyệt toàn bộ các gian hàng trong siêu thị Xem một gian hàng với danh sách các mặt hàng có trong gian hàng Xem chi tiết một sản phẩm trong gian hàng , đưa vào giỏ hàng. Quản lí các gian hàng, tạo một gian hàng Quản lí các sản phẩm (chỉnh sửa thông tin sản phẩm, xóa sản phẩm), tạo sản phẩm Quản lí các chọn lựa cách thức giao hàng (xóa, thêm cách thức giao hàng) Quản lí các đơn đặt hàng (xem chi tiết đơn hàng) Mudule tin tức – bài báo - blog Xem toàn bộ bài báo Xem theo đầu mục, chọn rss Xem chi tiết , viết lời bình và đánh giá bài báo Quản lí các đầu mục, tạo đầu mục Quản lí các bài báo (chỉnh sửa, xóa), tạo bài báo mới Quản lí các nhận xét bài báo Module chưng cầu ý kiến khách hàng Xem các chưng cầu - polls, cho ý kiến (vote) Quản lí các chưng cầu (chuyển chưng cầu sang trạng thái đã lấy đủ ý kiến - archive, hiện hành, chỉnh sửa chưng cầu, xóa chưng cầu) , tạo chưng cầu mới Module gửi thư từ hệ thống Xem ,xóa các thư đã gửi Tạo thư và gửi Module định vị cửa hàng siêu thị gần nhất Tìm các siêu thị trong khoảng cách nhất định Tìm đường đi tới 1 siêu thị Thêm vị trí siêu thị mới vào hệ thống Module Forum Duyệt các diễn đàn. Xem các bài thảo luận, tham gia thảo luận , xác nhận thích hay không thích bài thảo luận, tạo bài thảo luận Quản lí các diễn đàn (chỉnh sửa , xóa diễn đàn) Quản lí các bài thảo luận (approve, đóng , xóa bài thảo luận) Module quốc tế hóa Hiển thị các thông tin địa phương ứng với thông tin địa phương trong hồ sơ người dùng như tiền dùng ở địa phương, thời gian, cách viết con số của địa phương,… Module kiểm thử Kiểm thử phần bài báo, blog Kiểm thử phần gửi thư CHƯƠNG 1: MỞ ĐẦU LÍ DO THỰC HIỆN ĐỀ TÀI Tổng quan về ASP.NET MVC Không phải tự nhiên mà rất nhiều web programming framework phổ biến nhất hiện nay kế thừa các nguyên tắc của MVC như Django, Ruby on Rails, CakePHP, Struts, … Sự thành công của việc kế thừa kiểu mẫu lập trình này cuối cùng cũng đã khiến Microsoft quyết định đưa các nghuyên tắc đó vào sử dụng trong .NET Framework và rồi hình thành nên ASP.NET MVC đầu năm 2007 phiên bản 1.0 . Kiểu mẫu lập trình các ứng dụng nền tảng Web này tuy rằng không phải là kiểu mẫu lập trình tốt nhất hiện nay xong nó có những ưu điểm nhất định và được sử dụng khá phổ biến và đang tiếp tục được hỗ trợ phát triển của Micorosoft. Chính vì những lí do này mà nó đáng được tìm hiểu để từ đó sử dụng một cách hiệu quả. Tổng quan về ứng dụng thương mại siêu thị trực tuyến Việc mua bán kinh doanh hiện nay đang ngày càng trở nên dễ dàng hơn với sự hỗ trợ của CNTT hiện tại có một số lượng rất lớn các website thương mại đã được xây dựng và sử dụng . Việc này giúp đa dạng hóa các phương thức bán hàng và vì vậy hàng hóa được tiêu thụ dễ dàng hơn. Có rất nhiều ích lợi từ việc kinh doanh trực tuyến có thể thấy được như: Đối với khách hàng: Mua hàng ở bất kể đâu miễn là họ có máy tính kết nối mạng. Nhanh chóng tìm kiếm được mặt hàng cần mua chỉ qua vài click chuột. Không phải đối mặt với nhân viên bán hàng. … Đối với doanh nghiệp: Có thêm được một kênh quảng bá sản phẩm hiệu quả mà chi phí thấp. Nắm bắt được thông tin phong phú về thị trường và đối tác. Thiết lập được mối quan hệ tốt với khách hàng và đối tác. Đa dạng hóa các kênh bán hàng của doanh nghiệp. …. Hệ thống siêu thị trực tuyến là một ví dụ điển hình về thương mại điện tử nó có đầy đủ các module mà một hệ thống thương mại điện tử cần có. Trong chuyên đề tốt nghiệp của mình em sẽ xây dựng hệ thống bán hàng trực tuyến theo mô hình MVC dựa trên nền tảng công nghệ .NET. SƠ LƯỢC VỀ YÊU CẦU KIẾN TRÚC CỦA HỆ THỐNG Yêu cầu đặt ra cho kiến trúc hệ thống Xây dựng nên hệ thống siêu thị trực tuyến cấu thành từ các module riêng biệt đồng thời quản lí nội dung động như bài báo,diễn đàn, phiếu điều tra (polls) và gửi thư từ hệ thống thì cần giải quyết các vấn đề chung đặt ra với mỗi module đó là: Tách biệt mã lệnh truy cập CSDL với mã lệnh logic nghiệp vụ và mã lệnh cho giao diện để hệ thống có thể dễ dàng bảo trì và mở rộng . Cô lập kiến trúc truy cập CSDL để từ đó có thể hỗ trợ việc lưu trữ với các CSDL quan hệ khác nhau như SQL , MySQL , Oracle,.. mà không phải thực hiện thay đổi nào với tầng đối tượng nghiệp vụ. Ngược lại việc thay đổi tầng trình diễn (giao diện người dùng) hay tầng đối tượng nghiệp vụ cũng không làm thay đổi các tầng còn lại – Tạo ra tính decoupling cho hệ thống. Thiết kế kiến trúc đối tượng nghiệp vụ lấy từ tầng truy cập CSDL theo kiểu hướng đối tượng bằng cách ánh xạ cơ sở dữ liệu quan hệ vào các lớp OOP. Hỗ trợ cơ chế caching với các đối tượng nghiệp vụ .Điều này giúp làm giảm việc sử dụng CPU, nguồn CSDL, băng thông mạng – network bandwidth và như vậy làm tăng hiệu năng chung của hệ thống. Tạo tệp cấu hình cho các module để có thể dễ dàng thay đổi chúng. Thiết kế kiến trúc hệ thống Với các dự án ASP.NET web forms truyền thống ta sẽ không bao giờ thực sự tách riêng được phần giao diện người dùng với logic ứng dụng bởi vì .NET cung cấp sẵn các điều khiển phía máy chủ (server side control) mà ta thường sử dụng bằng cách kéo thả vào các Web Form ví dụ như GridView. Các control kiểu này giúp chúng ta thực hiện khá nhiều việc tuy nhiên lại thường làm cho mã lệnh xử lí logic của ứng dụng lẫn vào với mã lệnh giao diện. Một ví dụ thường thấy đó là việc tạo ra logic để sắp xếp các GridView hay lọc dữ liệu ứng với một sự kiện nhấn chuột. Khi sử dụng những control như thế ta luôn cần có các tệp mã lệnh để xử lí logic đằng sau (code behind file) các view. Chúng ta có thể tự tạo trọn vẹn một ứng dụng có tính module tuy nhiên điều này dẫn tới việc ta phải tự xây dựng framework cho ứng dụng của mình. Với việc ra đời của framework asp.net mvc thì mọi thứ đã thay đổi hẳn không còn các tệp mã lệnh xử lí logic đằng sau các view giúp tách biệt hoàn toàn giữa giao diện với logic nghiệp vụ của ứng dụng. Với asp.net mvc framework ta có thể dễ dàng, nhanh chóng phát triển kiến trúc ứng dụng theo hướng phân tầng (n-tier web application) Thiết kế kiến trúc phân tầng Với ứng dụng siêu thị trực tuyến ta sẽ chia thành các tầng như sau: Tầng lưu trữ dữ liệu: Nơi lưu trữ CSDL. CSDL của ứng dụng là CSDL quan hệ Tầng truy cập CSDL (Data Access Layer – DAL): Mã lệnh để lấy dữ liệu , xử lí dữ liệu thô được lưu trong tầng lưu trữ DL. Nhiệm vụ của tầng này là đưa ra các truy xuất CSDL theo logic nghiệp vụ và có tính trực quan hơn cho ứng dụng. Che dấu các chi tiết thâm nhập CSDL ở mức thấp tăng tính an toàn cho CSDL của hệ thống. Tầng nghiệp vụ (Business Logic Layer – BLL): Mã lệnh ở tầng này sẽ thực hiện các nguyên tắc nghiệp vụ , tạo các đối tượng cụ thể thuộc miền nghiệp vụ nhằm thỏa mãn các yêu cầu của ứng dụng. Tầng Logic ứng dụng (Application Logic Layer): Mã lệnh thuộc tầng này sẽ xử lí các tương tác giữa tầng trình diễn (Presentation Layer) với tầng logic nghiệp vụ Tầng trình diễn (Presentation Layer – PL) : Là các đoạn mã lệnh tạo nên những gì mà người dùng thấy ở trên trình duyệt – nó có thể là các dữ liệu đã được định dạng, thực đơn duyệt hệ thống, … Hình 1.1 – Kiến trúc ứng dụng Lựa chọn lưu trữ dữ liệu và thiết kế tầng truy xuất dữ liệu Ở ứng dụng này ta lựa chọn lưu trữ CSDL với SQL server 2008. SQL server 2008 tuy nhiên trong thực tế có thể khách hàng lại muốn sử dụng Oracle server hay IBM DB2 server … vì nhiều lí do như họ muốn tích hợp ứng dụng của bạn vào một project lớn hơn mà project lại dùng Oracle Server để lưu trữ DL nên tầng truy xuất dữ liệu cần được thiết kế sao cho đủ linh hoạt để việc thay đổi diễn ra dễ dàng , nhanh chóng. Tầng truy xất dữ liệu là mã lệnh thực hiện truy vấn CSDL để lấy dữ liệu, cập nhật, chèn, hay xóa dữ liệu. Đây là mã lệnh gần với CSDL nhất , sử dụng tất cả những gì có trong CSDL từ giản đồ các bảng,các stored procdure … cho đến các trường. Mã lệnh truy xuất tới CSDL cụ thể phải được tách biệt với mã lệnh các trang web vì các lí do như: Người viết code cho phần giao diện và người viết code cho phần truy cập CSDL có thể không phải là một. Với những ứng dụng nền tảng web cỡ vừa và lớn thì thường có nhiều lập trình viên tham gia xây dựng. Người xây dựng giao diện sẽ hầu như không quan tâm tới CSDL. Tuy nhiên họ vẫn cung cấp giao diện hiển thị thông tin từ CSDL bởi lẽ tất cả các chi tiết hiển thị đã được gói gọn trong các đối tượng riêng biệt cung cấp việc truy vấn CSDL ở mức cao – trừu tượng hơn. Một vài truy vấn CSDL sẽ được sử dụng ở một số các trang của ứng dụng. Nếu như ta đặt trực tiếp truy vấn vào các trang này mà sau đó ta muốn thay đổi một câu lệnh truy vấn như thay đổi cách thức sắp xếp , hoặc thay đổi các trường dữ liệu ta sẽ phải xem lại toàn bộ code xem nơi nào sử dụng chúng và thực hiện thay đổi. Nếu như ta đưa mã lệnh truy cập CSDL vào một vài lớp trong tầng truy xuất CSDL ta sẽ chỉ phải thay đổi các file này mà không cần quan tâm tới các trang web. Code truy vấn CSDL mà đặt trong các trang web sẽ làm khiến ta khó khăn trong việc thay đổi sang CSDL quan hệ khác Với asp.net mvc framework việc tách biệt tầng truy xuất CSDL là gần như bắt buộc. Tất cả các đối tượng liên quan tới tầng truy xuất CSDL nên được đặt trong thư mục models. Để xử lí việc hỗ trợ lưu trữ dữ liệu linh động với các Database Server khác nhau ta sẽ sử dụng kiểu mẫu thiết kế Provider Model cụ thể là thay vì việc viết trực tiếp các lớp truy cập CSDL (DAL classes) đầu tiên ta sẽ viết lớp trừu tượng (abstract class) và giao diện công khai của các lớp đó (public interface) và cả các phương thức hỗ trợ nếu cần. Mã lệnh thực sự dùng truy vấn CSDL sẽ nằm ở lớp kế thừa trực tiếp từ lớp trừu tượng và lớp này sẽ đưa ra các mã lệnh cụ thể cho các phương thức trong interface của lớp trừu tượng. Lớp trừu tượng được gọi là Provider và đây là lí do vì sao Micorsoft gọi kiểu mẫu thiết kế này là Provider Model . Rõ ràng khi thay đổi Database Server ta chỉ việc thay đổi CSDL , thay đổi các lớp thứ 2 này là xong . Hơn thế nữa với lớp trừu tượng ta biết rõ được phải xây dựng lớp thứ 2 như thế nào và chúng ta không hề phải thay đổi các module khác ngoại trừ module DAL. Trong trường hợp buộc phải thêm các lớp provider mới thì ta có thể biến các lớp provider cũ thành virtual để từ đó cho phép các lớp provider mới có các hàm quá tải (override) các hàm của các provider cũ. Hình dưới đây minh họa mối quan hệ giữa các tầng giao diện, logic nghiệp vụ, truy cập CSDL và lưu trữ CSDL Hình 1.2 – Mối quan hệ giữa các tầng giao diện, logic nghiệp vụ, truy cập CSDL và lưu trữ CSDL Sử dụng LINQ ở tầng truy cập CSDL (DAL) Với .NET Framework 3.5 chúng ta được cung cấp một tính năng mới là LINQ (Language Intergrated Query) . LINQ được thiết kế để cung cấp cách thực hiện truy vấn trên bất kể kiểu collection nào trong .NET Framework bao gồm mảng, dictionaries, lists, XML, và các CSDL,… LINQ có những tính năng đáng để xem xét dù ta có thể đơn giản sử dụng typed data sets hay các thực thể ta tạo ra để thực hiện việc xây dựng tầng truy vấn CSDL theo mong muốn như: Sử dụng các cấu trúc truy vấn giống với SQL trong mã lệnh .NET hay xem đoạn code này: SQL select c.* from Customer as c LINQ var customers = from c in dataContext.Customers select c; 2 câu lệnh trên thực thi cùng một nhiệm vụ là truy vấn thông tin khách hàng từ bảng Customers, tuy nhiên trong cách truy vấn sử dụng LINQ thì dữ liệu được chuyển thành dạng collection, gọi là Iqueryable, giúp ta có thể sử dụng vòng lặp chuẩn foreach . Trong C# ta thấy có từ khóa mới là var – là kiểu dữ liệu nạc danh (kiểu dữ liệu này chỉ được xác định khi biên dịch – trình biên dịch sẽ biết nó có kiểu gì khi biên dịch và thay từ khóa var với tên kiểu đúng của nó) Với LINQ có thể sử dụng kiểu dữ liệu an toàn (type safety) và các truy vấn đa dạng hơn. Hãy xem câu lệnh truy vấn xem mỗi khách hàng trả bao nhiêu cho các đơn đặt hàng của họ trong năm 2008 bằng SQL và LINQ: SQL select c.CustomerId, c.Name as CustomerName, sum(o.TotalPaid) as TotalPaid from Customer as c inner join Orders as o on c.CustomerId = o.CustomerId where o.OrderDate >= ‘1/1/2008’ and o.OrderDate <= ‘12/31/2008’ group by c.CustomerId, c.Name LINQ var customers = from c in dataContext.Customers join o in dataContext.Orders on c.CustomerId equals o.CustomerId where c.OrderDate >= new Date(2008, 1, 1) && c.OrderDate <= new Date(2008, 12, 31) group o by new { c.CustomerId, c.Name } into g select new { CustomerId = c.CustomerId, CustomerName = c.Name, TotalPaid = g.Sum(o => o.TotalPaid) }; Trong ví dụ với LINQ thì trường OrderDate có kiểu là System.DateTime. Vì vậy nếu ta so sánh nó với dữ liệu kiểu chuỗi, hay bất kể một đối tượng nào không phải kiểu DateTime thì trình biên dịch sẽ báo lỗi và cho biết chính xác vị trí lỗi xảy ra đây là một ích lợi khi sử dụng LINQ. Ví dụ dưới đâu sẽ cho thấy với LINQ ta có các truy vấn tiện ích hơn so với SQL ví dụ như lấy một oder có ID = 2, lấy đối tượng OderInformation đổi ngày thêm vào rồi lưu oder này lại vào CSDL . var order = (from o in dataContext.Orders where o.OrderId == 2 select o).FirstOrDefault(); foreach(var orderInformation in order.OrderInformation) orderInformation.DateAdded = DateTime.Now; dataContext.SubmitChanges(); LINQ-to-SQL : Để LINQ có thể được sử dụng ta cần phải khởi tạo model tương ứng . Việc tạo model này hoàn toàn có thể thực hiện bằng cách kéo thả các bảng cũng như store procedure vào phần designer . Các model sẽ được tạo tự động. Việc tạo model như vậy giúp xây dựng ứng dụng nhanh chóng tuy nhiên lại là hạn chế vì chỉ có thể sử dụng với SQL server. Thiết kế tầng logic nghiệp vụ Tầng truy cập CSDL được xây dựng với LINQ-to-SQL trả về các tập hợp của các lớp thực thể chứa các trường của dữ liệu từ bảng DL. Tuy nhiên dữ liệu trả về của tầng này vẫn là dữ liệu thô dù chúng được chứa trong các lớp bởi vì chúng được sinh tự động. Chúng chỉ là nơi chứa các dữ liệu có định kiểu mạnh được sử dụng để truyền dữ liệu giữa các tầng lưu trữ dữ liệu và tầng logic nghiệp vụ. Tầng nghiệp vụ logic sử dụng các dữ liệu này chuyển chúng tới tầng giao diện người dùng . Ở tầng này có thể có thêm kiểm định logic, xây dựng các biểu thức tính toán dựa trên các thuộc tính, chuyển một số thuộc tính thành riêng tư (private) hay chỉ đọc (read-only) thêm các phương thức tĩnh, thực thể để xử lí việc xóa, chỉnh sửa, truy xuất dữ liệu. Việc tạo các dữ liệu hướng đối tượng , định kiểu mạnh tạo ra sự trừu tượng hóa mạnh mẽ cô lập tầng lưu trữ CSDL cung cấp cho lập trình viên phát triển giao diện tập hợp các lớp đơn giản , xong hiệu quả khi sử dụng. Với asp.net mvc framework thì tầng BLL nằm gọn trong các điều khiển (controllers) và các lớp điều khiển bắt buộc phải được đặt trong thư mục controllers giúp tách hẳn tệp code behind ra khỏi views. Các lớp này sử dụng các tập hợp (collections) xử lí dữ liệu thông qua chúng trả lại view tương ứng. Với việc tạo tầng logic nghiệp vụ ở thư mục controllers trong ứng dụng .net mvc thì ta có được một số ích lợi như: Tạo được các kiểm định bộ phận (unit testing ) cho ứng dụng bởi lẽ tấc cả các logic nghiệp vụ được đặt trong thư mục controllers hoặc các thực thể mà ta tạo ra. Không có các trình điều khiển phía máy chủ và như vậy các kiểm định bộ phận không bị lỗi. Bảo mật dễ dàng hơn bởi lẽ tất cả các xử lí được đặt trong các lớp điều khiển ta không còn phải tách biệt các trang cần có cấp phép cao hơn và đưa ra lệnh chứng thực cấp phép với thư mục chứa các trang này. Ta có thể trả về nhiều views từ một hành động trong controller. Với vấn đề bảo mật ta chỉ cần xử lí đơn giản như trong đoạn code sau: [Authorize (Roles = “Admin”)] public ActionResult EditContent (int id, ..) { //logic dùng chỉnh sửa nội dung return View(); } Xây dựng vùng nhớ đệm - caching , sử dụng mạng phân phối nội dung tăng hiệu năng của hệ thống Với những ứng dụng nền tảng web có những dữ liệu ít thay đổi và thường được yêu cầu bởi nhiều người dùng và trong hệ thống siêu thị trực tuyến các dữ liệu như vậy có thể kể tới như danh sách các đầu mục bài báo, đầu mục sản phẩm và các sản phẩm… Để có thể tăng hiệu ứng cho hệ thống với những dữ liệu kiểu này ta có thể tạo vùng nhớ đệm cho chúng và giữ vùng nhớ đệm này ở bộ nhớ trong những khoảng thời gian xác định. Asp.net framework cũng khá mạnh trong việc giúp chúng ta xây dựng , quản lí vùng nhớ đệm ở trong các lớp điều khiển. Hãy xem một ví dụ sau cho thấy việc tạo vùng nhớ đệm và duy trì nó trong 60 giây [OutputCache (Duration = 60)] public ActionResult SomeControllerMethod() { //logic nghiệp vụ } Trong trường hợp hệ thống được truy cập bởi một lượng lớn người dùng mỗi ngày (vài ngàn đến vài chục ngàn) ta có thể tính tới việc sử dụng các mạng phân phối nội dung (Content Delivery Networks) đây là mạng phát tán của các máy chủ (distributed network of servers) giúp ta giải quyết vấn đền quá tải truy cập nội dung của hệ thống, giúp người dùng truy cập nội dung hệ thống nhanh hơn . Các mạng này thực hiện công việc phôn phối nội dung thông qua mạng của các máy chủ DNS hướng yêu cầu của người dùng tới một máy chủ có sẵn nội dung người dùng cần và gần người này nhất. Nội dung cần phân phối có thể là các file ảnh, javascript, css, tài liệu, phim ảnh , âm thanh … với hệ thống siêu thị trực tuyến này thì các tệp nội dung được đặt trong thư mục Content việc đưa tất cả các file nội dung vào thư mục này sẽ rất phù hợp với cơ chế làm việc của CDN ở chỗ để CDN hoạt động ta cần phải cài phần mềm tương ứng vào máy chủ web chứa hệ thống và cấu hình để phần mềm này biết cụ thể vị trí của nội dung cần phân phối việc đưa nội dung cần phân phối vào một thư mục sẽ dễ dàng cho việc cấu hình và quá trình lookup của phần mềm này. Về mặt giá cả thì giải pháp sử dụng CDN sẽ rẻ hơn nhiều so với việc thuê đường truyền có băng thông lớn hơn. Tầng trình diễn (UI hay Views) Tầng trình diễn tương ứng với các trang Web Forms duy chỉ có điều là không có các tệp mã lệnh đằng sau mỗi web form. Ta đã biết các tệp mã lệnh đó được thay thế bằng những lớp điều khiển (Controller classes) . Tuy nhiên còn các server control (được cung cấp bởi Microsoft , hoặc các hãng thứ 3) vẫn thường được sử dụng trong các asp.net web forms trước kia thì sao? Trên thực tế những control kiểu này không còn cần thiết trong các web forms nữa. ASP.NET MVC framework đem đến rất nhiều lựa chọn thay thế cho các control đó mà trong hệ thống siêu thị trực tuyến sử dụng . Framework này có một tập các phương thức mở rộng hỗ trợ HTML giúp tự động tạo và gắn kết dữ liệu của khá nhiều controls mà ta vẫn sử dụng với ASP.NET web forms. Ví dụ về tạo textbox và tiêu đề của textbox này được ánh xạ bởi từ điển ViewData . Title Khi đoạn code trên được chuyển tới trình duyệt thì nó được chuyển thành đoạn code HTML tương ứng sau: Title Đoạn code này đơn giản chỉ có thẻ input mà hoàn toàn không có liên quan tới thuộc tính runat. Đối với trình duyệt input này được viết hoàn toàn bằng HTML điều này giúp tối ưu hóa khả năng tìm kiếm hệ thống của các hệ thống tìm kiếm như google hay bing giúp các khách hàng chưa biết tới hệ thống có thể tìm đến hệ thống thông qua cỗ máy tìm kiếm Ở tầng này của hệ thống siêu thị trực tuyến có sử dụng javascript ở để xử lí các sự kiện không đồng bộ phía người dùng. Cụ thể ta sẽ sử dụng JQuery – đây là javascript framework ổn định , ra đời cách đây vài năm , có các tài liệu hỗ trợ đầy đủ , dễ sử dụng . CHƯƠNG 2: TÌM HIỂU ASP.NET MVC VÀ LINQ TỔNG QUAN VỀ ASP.NET MVC ASP.NET MVC là gì? Mô hình MVC cơ bản Tham khảo asp.net mvc của microsoft MVC viết tắt của các chữ cái đầu của Models, Views, Controllers. MVC chia giao diện UI thành 3 phần tương ứng: đầu vào của controller là các điều khiển thông qua HTTP request, model chứa các miền logic, view là những thứ được sinh ra trả về cho trình duyệt. Hình 2.1 – Mô hình MVC cơ bản Lợi ích của việc dùng phương pháp MVC là sự phân đoạn rõ ràng giữa models, views, controllers bên trong ứng dụng. Cấu trúc sạch sẽ giúp cho việc kiểm tra lỗi ứng dụng trở nên dễ dàng hơn. Một vài đặc tính của ASP.NET MVC Tách rõ ràng các mối liên quan, mở khả năng test TDD (test driven developer). Có thể test unit trong ứng dụng mà không cần phải chạy Controllers cùng với tiến trình của ASP.NET và có thể dùng bất kỳ một unit testing framework nào như NUnit, MBUnit, MS Test, v.v… Có khả năng mở rộng, mọi thứ trong MVC được thiết kế cho phép dễ dàng thay thế/tùy biến ( ví dụ: có thể lựa chọn sử dụng engine view riêng, routing policy, parameter serialization, v.v…). Bao gồm một ánh xạ URL mạnh mẽ cho phép xây dựng ứng dụng với những URL sạch, các URL không cần cs phần mở rộng ( ví dụ: có thể ánh xạ địa chỉ /Products/Edit/4 để thực hiện hành động “Edit” của lớp điều khiển ProductControllers hoặc ánh xạ địa chỉ /Blog/SomeTopic để thực hiện hành động “Display Topic” của lớp điều khiển BlogEngineController ) ASP.NET MVC Framework cũng hỗ trợ những file ASP.NET như .ASPX .ASCX và .Master, đánh dấu các tập tin này như một “view template” ( có thể dễ dàng dùng các tính năng của ASP.NET như lồng các trang Master, snippets, mô tả server controls, template, data-binding, localization, v.v… ). Tuy nhiên sẽ không còn postback và interactive back server và thay vào đó là interactive end-user tới một Controller class ( không còn viewstate, page lifecycle ) ASP.NET MVC Framework hỗ trợ đầy đủ các tính năng bảo mật của ASP.NET như forms/windows authenticate, URL authorization, membership/roles, output và data caching, session/profile state, configuration system, provider architecture v.v… Sự khác biệt so với Web Form ASP.NET WebForm sử dụng ViewState để quản lý, các trang ASP.NET đều có lifecycle, postback và dùng các web controls, các events để thực hiện các hành động cho UI khi có sự tương tác với người dùng nên hầu hết ASP.NET WebForm xử lý chậm. ASP.NET MVC Framework chia ra thành 3 phần: Models, Views, Controllers. Mọi tương tác của người dùng với Views sẽ thực hiện hành động trong Controllers, không còn postback, không còn lifecycle hay events nữa. Việc kiểm tra ( test ), gỡ lỗi ( debug ) với ASP.NET đều phải chạy tất cả các tiến trình của ASP.NET và mọi sự thay đổi ID của bất kỳ controls nào cũng ảnh hưởng đến ứng dụng. Đối với ASP.NET MVC Framework thì việc có thể sử dụng các unit test có thể kiểm định khá dễ dàng hoạt động của các controller. Tính năng ASP.NET 2.0 ASP.NET MVC Kiến trúc chương trình Kiến trúc mô hình WebForrm à Bussiness à Database Kiến trúc sử dụng việc phân chia chương trình thành Controllers, Models, Views Cú pháp chương trình Sử dụng cú pháp của webform. Tất cả các sự kiện và controls đều do server quản lí Các sự kiện được điều khiển bởi Controllers, các controls không do server quản lí Truy cập dữ liệu Sử dụng hầu hết các công cụ truy cập dữ liệu trong ứng dụng Phần lớn dùng LINQ to SQL class để tạo mô hình truy cập đối tượng Debug Debug chương trình phải thực hiện tất cả bao gồm các lớp truy cập dữ liệu , sự hiển thị, điều khiển các controls Debug có thể sử dụng các unit test kiểm tra các phương thức trong controller Tốc độ phân tải Tốc độ phân tải chậm trong khi trang có quá nhiều controls vì ViewState quá lớn Phân tải nhanh hơn do không phải quản lý ViewState để quản lý các control trong trang Tương tác với javascript Tương tác với javascript khó khăn vì các controls bị quản lí bởi server Tương tác với javascript dễ dàng vì các đối tượng không do server quản lí dẫn tới điều khiển không khó khăn URL address Cấu trúc địa chỉ URL có dạng .aspx?& Cấu trúc địa chỉ URL rành mạch theo dạng Controllers /Action/ Id Quá trình thực thi một ứng dụng nền web ASP.NET MVC Một yêu cầu gửi tới ứng dụng nền tảng web viết bằng ASP.NET MVC đầu tiên sẽ đi qua đối tượng UrlRoutingModule, đây là một module của HTTP. Module này sẽ phân tích yêu cầu và thực thi việc chọn lựa định tuyến. Nó sẽ chọn đối tượng route đầu tiên thích hợp với yêu cầu hiện thời. (Đối tượng route là lớp thực thi BaseUrl , là một thể hiện cụ thể của lớp Route) Nếu không có định tuyến nào thích hợp đối tượng UrlRoutingModule sẽ không thực hiện việc định tuyến mà trả yêu cầu đó về cho IIS hay ASP.NET xử lí. Từ đối tượng Route được chọn lựa, đối tượng UrlRoutingModule nhận lấy đối tượng IRouteHandler tương ứng với đối tượng Route. Cụ thể trong một ứng dụng ASP.NET MVC thì IRouteHandler sẽ là một thể hiện của MvcRouteHandler. Thể hiện này sẽ tạo ra một đối tượng IHttpHandler và truyền nó tới đối tượng IHttpContext . Theo mặc định,thể hiện IHttpContext của MVC là đối tượng MvcHandler . Đối tượng này sẽ chọn ra controller đáp ứng yêu cầu gửi tới ứng dụng. Chú ý: Khi một ứng dụng nền Web viết bằng ASP.NET MVC chạy trên IIS 7.0 , phần mở rộng tên file là không cần thiết đối vớp dự án ứng dụng đó. Tuy nhiên nến chạy trên IIS 6.0 , IIS 6.0 yêu cầu bạn phải chỉ ra cho ASP.NET ISAPI DLL nhận các file có phần mở rộng .mvc Module nói trên và IIS là các điểm đầu vào đối với ASP.NET MVC framework . Chúng thực hiện những việc sau: Chọn ra một controller tương ứng xử lí yêu cầu trong một ứng dụng MVC nền web. Có được một thể hiện cụ thể của controller đó. Gọi phương thức Execute của controller này. Bảng sau sẽ liệt kê các giai đoạn thực thi của một dự án nền Web ASP.NET MVC Giai đoạn Chi tiết Nhận yêu cầu đầu tiên của ứng dụng Trong file Global.asax, các đối tượng Route được thêm vào đối tượng RouteTable Thực thi việc định tuyến Module UrlRoutingTable sử dụng đối tượng Route đầu tiên thích hợp trong tập RouteTable để tạo đối tượng RouteData . Đối tượng này sau đó lại tạo ra đối tượng RequestContext (IHttpContext). Tạo ra trình xử lí yêu cầu ứng dụng Đối tượng MvcRouteHandler tạo ra một thể hiện của lớp MvcHandler và truyền vào nó thể hiện RequestContext Tạo ra controller Các đối tượng MvcHandler sử dụng thể hiện của RequestContext để xách định đối tượng IcontrollerFactory qua đó tạo ra thể hiện controller Thực thi Controller Thể hiện MvcHandler gọi phương thức Execute của controller. Triệu gọi action Hầu hết các controllers đều kế thừa từ lớp Controller cơ bản. Đối với các controller như thế đối tượng ControllerActionInvoker đi kèm với controller đó sẽ xác định phương thức action của lớp controller đó để gọi, và rồi gọi phương thức đó. Thực thi kết quả Một phương thức action cụ thể mà nhận được dữ liệu từ người dùng sẽ chuẩn bị dữ liệu trả về thích hợp. Những kiểu dữ liệu trả về được xây dựng sẵn có thể được trả về gồm có: ViewResult (Cái thể hiện một View và là dữ liệu thường được trả về nhất), RedirectToRouteResult, RedirectResult…. LINQ TO SQL Tham khảo Linq to SQL là gì? LINQ to SQL là một phiên bản hiện thực hóa của O/RM (object relational mapping) có bên trong .NET Framework bản “Orcas” (nay là .NET 3.5), nó cho phép bạn mô hình hóa một cơ sở dữ liệu dùng các lớp .NET. Sau đó bạn có thể truy vấn cơ sở dữ liệu (CSDL) dùng LINQ, cũng như cập nhật/thêm/xóa dữ liệu từ đó. LINQ to SQL hỗ trợ đầy đủ transaction, view và các stored procedure (SP). Nó cũng cung cấp một cách dễ dàng để thêm khả năng kiểm tra tính hợp lệ của dữ liệu và các quy tắc vào trong mô hình dữ liệu của bạn. Mô hình hóa CSDL dùng Linq to SQL Visual Studio “Orcas” đã tích hợp thêm một trình thiết kế LINQ to SQL như một công cụ dễ dàng cho việc mô hình hóa một cách trực quan các CSDL dùng LINQ to SQL. Bài viết sau sẽ đi sâu hơn vào cách dùng trình thiết kế này (bạn cũng có thể xem đoạn video này để xem cách tôi tạo một mô hình LINQ to SQL). Bằng cách dùng trình thiết kế LINQ to SQL, tôi có thể dễ dàng tạo một mô hình cho CSDL mẫu “Northwind” giống như dưới đây: Hình 2.2 – Mô hình LINQ to SQL Mô hình LINQ to SQL ở trên định nghĩa bốn lớp thực thể: Product, Category, Order và OrderDetail. Các thuộc tính của mỗi lớp ánh xạ vào các cột của bảng tương ứng trong CSDL. Mỗi instance của một lớp biểu diễn một dòng trong bảng dữ liệu. Các mũi tên giữa bốn lớp thực thể trên biểu diễn quan hệ giữa các thực thể khác nhau, chúng được tạo ra dựa trên các mối quan hệ primary-key/foreign-key trong CSDL. Hướng của mũi tên chỉ ra mối quan hệ là một – một hay một – nhiều. Các thuộc tính tương ứng sẽ được thêm vào các lớp thực thể trong các trường hợp này. Lấy ví dụ, lớp Category ở trên có một mối quan hệ một nhiều với lớp Product, điều này có nghĩa nó sẽ có một thuộc tính “Categories” là một tập hợp các đối tượng Product trong Category này. Lớp Product cũng sẽ có một thuộc tính “Category” chỉ đến đối tượng ”Category” chứa Product này bên trong. Bảng các phương thức bên tay phải bên trong trình thiết kế LINQ to SQL ở trên chứa một danh sách các SP để tương tác với mô hình dữ liệu của chúng ta. Trong ví dụ trên tôi đã thêm một thủ tục có tên “GetProductsByCategory”. Nó nhận vào một categoryID và trả về một chuỗi các Product. Chúng ta sẽ xem bằng cách nào có thể gọi được thủ tục này trong một đoạn code bên dưới. Tìm hiểu lớp DataContext Khi ta bấm nút “Save” bên trong màn hình thiết kế LINQ to SQL, Visual Studio sẽ lưu các lớp .NET biểu diễn các thực thể và quan hệ bên trong CSDL mà chúng ta vừa mô hình hóa. Cứ mỗi một file LINQ to SQL chúng ta thêm vào solution, một lớp DataContext sẽ được tạo ra, nó sẽ được dùng khi cần truy vấn hay cập nhật lại các thay đổi. Lớp DataContext được tạo sẽ có các thuộc tính để biểu diễn mối bảng được mô hình hóa từ CSDL, cũng như các phương thức cho mỗi SP mà chúng ta đã thêm vào. Lấy ví dụ, dưới đây là lớp NorthwindDataContext được sinh ra dựa trên mô hình chúng ta tạo ra ở trên: Hình 2.3 – Mô hình NorthwindDataContext Các ví dụ Linq to SQL Lấy sản phẩm từ CSDL: Đoạn lệnh dưới đây dùng cú pháp LINQ để lấy về Ienumerable các đối tượng Product. Các sản phẩm lấy ra phải thuộc phân loại “Beverages”: Hình 2.4 – Hình minh họa lấy sản phẩm từ CSDL với LINQ Cập nhật một sản phẩm trong CSDL: Đoạn lệnh dưới đây cho thấy cách lấy một sản phẩm , cập ạnhật li giá tiền và lưu lại CSDL. Hình 2.5 – Hình minh họa cập nhật sản phẩm từ CSDL với LINQ Chèn thêm một phân loại mới và 2 sản phẩm vào CSDL: Đoạn mã dưới đây biểu diễn cách tạo một phân loại mới, và tạo hai sản phẩm mới , đưa chúng vào trong phân loại đã tạo rồi đưa vào CSDL. Chú ý rằng ta không cần phải tự quản lý các mối quan hệ primary key/foreign key, thay vào đó, chỉ đơn giản thêm các đối tượng Product vào tập hợp Products của đối tượng category, và rồi thêm đối tượng category vào tập hợp Categories của DataContext, LINQ to SQL sẽ biết cách thiết lập các giá trị primary key/foreign key một cách thích hợp. Hình 2.6 – Chèn sản phẩm mới vào CSDL Xóa các sản phẩm: Đoạn mã sau sẽ biểu diễn cách xóa tất cả các sản phẩm Toy khỏi CSDL: Hình 2.7 – Xóa một sản phẩm Gọi một thủ tục: Đoạn mã dưới đây biểu diễn cách lấy các thực thể Product mà không dùng cú pháp của LINQ, mà gọi đến thủ tục “GetProductsByCategory” chúng ta đã thêm vào trước đây. Nhớ rằng một khi đã lấy về kết quả, ta có thể cập nhật/xóa và sau đó gọi db.SubmitChanges() để cập nhật các thay đổi trở lại CSDL. Hình 2.8 – Gọi một thủ tục Lấy các sản phẩm và phân trang: Đoạn mã dưới đây biểu diễn cách phân trang trên server như một phần của câu truy vấn LINQ. Bằng cách dùng các toán tử Skip() và Take(), chúng ta sẽ chỉ trả về 10 dòng từ CSDL – bắt đầu từ dòng 200. Hình 2.9 – Lấy các sản phẩm và phân trang Tổng kết LINQ to SQL cung cấp một cách hay, rõ ràng để mô hình hóa lớp dữ liệu trong ứng dụng của bạn. Một khi đã định nghĩa mô hinh dữ liệu, bạn có thể dễ dàng thực hiện các câu truy vấn cũng như cập nhật, xóa, sửa dữ liệu một cách hiệu quả. CHƯƠNG 3: PHÂN TÍCH THIẾT KẾ ỨNG DỤNG ĐẶC TẢ CÁC CHỨC NĂNG HỆ THỐNG Thiết kế hệ thống Tham khảo và mô phỏng lại hầu hết các chức năng của các hệ thống như , Trong pha thiết kế hệ thống ta sẽ lần lượt làm rõ các module của hệ thống và mối quan hệ giữa các module. Các module của hệ thống Là một hệ thống siêu thị bán hàng trực tuyến thì về cơ bản hệ thống cần hỗ trợ người dùng duyệt , mua và thanh toán hàng . Bên cạnh đó người quản trị hệ thống , nhân viên siêu thị cần quản lí được hàng hóa (xem, thêm , xóa , sửa), quản lí thông tin đặt mua, thanh toán, vận chuyển hàng . Ngoài ra hệ thống còn có thêm một số module như module diễn đàn, module bài báo tin tức, module lấy ý kiến khách hàng, module gửi thư từ hệ thống, module định vị siêu thị, module quốc tế hóa. Hình 3.1 – Các module của hệ thống siêu thị trực tuyến Tất cả các module của hệ thống đều sử dụng module thành viên và hồ sơ . Ngoại trừ việc này ra thì chúng độc lập với nhau. Mối quan hệ giữa các module Tất cả các module của hệ thống là độc lập với nhau ngoại trừ việc chúng sử dụng module thành viên và hồ sơ cho việc xác thực vai trò người dùng (authenticate) và cấp phép (authorize) sử dụng chức năng cụ thể của một module ứng với vai trò đó. Việc tách biệt các module như vậy giúp hệ thống linh hoạt hơn khi nâng cấp, hay có sửa đổi nhỏ. Đặc tả sơ lược các module Module hồ sơ và thành viên Module này với người dùng là khách hàng thì họ có thể sử dụng để tạo tài khoản, đăng nhập , đăng xuất , quản lí hồ sơ còn đối với người quản trị hệ thống thì họ còn dùng vào việc quản lí vai trò người dùng, xóa, tạo vai trò, quản lí người dùng. Module lấy ý kiến khách hàng Với module lấy ý kiến khách hàng với vai trò admin người dùng có thể tạo các câu hỏi để lấy ý kiến khách hàng qua đó nắm bắt được các thông tin cần thiết để phát triển việc kinh doanh của công ty, hay phát triển hệ thống để phục vụ khách hàng tốt hơn. Người dùng là khách hàng có thể đóng góp ý kiến của mình để được phục vụ tốt hơn. Module thương mại Sử dụng module thương mại người dùng là khách hàng có thể xem , chọn mua sản phẩm, thanh toán qua paypal. Đối với người dùng có vai trò là quản lí siêu thị (store keeper) – người quản lí sẽ quản lí gian hàng mà họ tạo ra như quản lí sản phẩm , quản lí đơn đặt hàng.. Module gửi thư Khi sử dụng module này người dùng là khách hàng có thể đăng kí nhận thư của hệ thống thông qua mục nhận thư trong hồ sơ. Còn với người dùng có vai trò admin thì họ còn có thể tạo thư và gửi thư tới toàn bộ các thành viên có đăng kí nhận thư từ hệ thống. Module diễn đàn Người dùng nạc danh có thể duyệt xem bài bình luận trong các diễn đàn , tuy nhiên để gửi bài hay bình luận họ phải đăng nhập vào hệ thống. Chức năng quản lí diễn đàn, quản lí các bài thỏa luận dành cho người dùng có vai trò là editor. (biên tập) Module tìm kiếm siêu thị Người dùng có thể sử dụng module này để tìm kiếm các siêu thị gần nơi mình ở theo khoảng cách và tìm đường đi đến một siêu thị nào đó. Với người dùng có vai trò là admin thì còn có thêm chức năng thêm vị trí siêu thị mới vào hệ thống. Module bài báo, tin tức, và blog Người dùng sử dụng module này có thể xem toàn bộ các bài báo, xem theo đầu mục, xem chi tiết bài báo , viết lời bịnh và đánh giá . Người dùng với vai trò là editor có thêm các chức năng như quản lí các đầu mục báo, tạo đầu mục mới , xóa đầu mục đang có trong hệ thống. Quản lí các bài báo (chỉnh sửa, xóa), tạo bài báo mới Module quốc tế hóa Module này cho người dùng biết thông tin địa phương của người dùng qua thông tin ở hồ sơ đăng kí như thời gian, tiền dùng tại địa phương (chẳng hạn như ở Mĩ là USD, ở Việt nam là VND) Module kiểm thử Ở module này chỉ kiểm thử với hai module khác trong hệ thống là module bài báo , tin tức, blog và module gửi thư. MODULE HỒ SƠ NGƯỜI DÙNG VÀ THÀNH VIÊN Tổng quan về module Module sẽ sử dụng built-in ASP.NET 2.0 membership system (hệ thống quản lí thành viên xây dựng sẵn cho ASP.NET 2.0) cung cấp bởi Microsoft cho phép người dùng (user) thực hiện các chức năng gồm có đăng kí tài khoản, đăng nhập , thay đổi mật khẩu, lấy lại mật khẩu, nhập thông tin hồ sơ , cho phép nhà quản trị hệ thống (admin) đăng nhập vào hệ thống, quản lí quyền, quản lí người dùng (trong đó có xem toàn bộ người dùng trong hệ thống, tìm người dùng và xóa người dùng, thay đổi vai trò người dùng…). Trước khi lưu trữ mật khẩu người dùng vào CSDL để bảo mật tất cả các mật khẩu sẽ được thay đổi thông qua hàm băm . Hình 3.2 – Sơ đồ tổng quan người dùng và các chức năng của module Ghi chú: Module này được sử dụng bởi hầu hết các module khác như module cộng đồng, module tin tức bài báo, hay module đánh giá và cho điểm các luồng thảo luận trong diễn đàn,.. Sơ đồ chức năng Hình 3.3 – Sơ đồ tổng quan chức năng của module theo mô hình UC Các bảng DL của module Tạo bảng DL Sau khi tạo CSDL GlobalStore với SQL Server Management Studio , chạy file aspnet_regsql trong thư mục C:\Windows\Microsoft.NET\Framework\v2.0.50727 để tạo các bảng dành cho module thành viên và hồ sơ mà ASP.NET phiên bản 2.0 đã cung cấp sẵn. Ngoài ra ta xây dựng thêm bảng language để dùng cho việc tạo mục ngôn ngữ trong hồ sơ người dùng như sau: Hình 3.4 Bảng Langugage Cấu hình trong web.config cho membership, role và profile: Mục membership: <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider,System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="GlobalStoreConnectionString" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="GlobalStore" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/> Qua nội dung phần config có thể thấy ta dùng ASPNETSqlMembershipProvider phiên bản 2.0 . Mật khẩu trước khi lưu xuống CSDL được thay đổi thông qua hàm băm passwordFormat="Hashed",… Mục Role: <add connectionStringName="GlobalStoreConnectionString" applicationName="GlobalStore"name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider,System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/> Mục Profile: ASP.NET cung cấp một cơ chế có sẵn để quản lí hồ sơ người dùng. Ta chỉ cần khai báo các thông tin cần thiết lưu trong hồ sơ. Khi ứng dụng chạy thông tin thuộc tính của hồ sơ sẽ được thêm vào lớp Page . <add name="ShoppingCart" type="GlobalStore.Models.ShoppingCart" serializeAs="Binary" allowAnonymous="true"/> Models Lớp UserInformation Hệ thống sẽ có một số trang mà người dùng sẽ sử dụng liên quan với việc đăng nhập và quản lí tài khoản của họ. Để truyền dữ liệu mà không phải tạo một số lượng lớn biến cho mỗi phương trức hành động chúng ta tự tạo lớp UserInformation để dùng. Hình 3.5 Sơ đồ lớp UserInformation Lớp ProfileInformation Lớp này được dùng để quản lí thông tin hồ sơ đồng thời hỗ trợ để tạo các chọn lựa dropdown phần điền thông tin trên trang quản lí hồ sơ như nghề nghiệp, nước hiện tại ở, ngôn ngữ, … Các thuộc tính của lớp tương ứng với các thuộc tính khai báo trong web.config và các phương thức tạo dropdown tương ứng cho phần quản lí hồ sơ người dùng gồm có dropdown cho các mục: Country. Gender. Language. Occupation. Subscription. Hình 3.6 Sơ đồ lớp ProfileInformation Controllers Tương ứng với các chức năng của module ta các phương thức cho controller của module này như sau: Phương thức hành động Bảo mật Các tham số Logout ---- ---- Login ---- ---- Login_OnPost ---- string userName, string password, bool persistent, string returnUrl ForgotPassword ---- ---- ForgotPassword_OnPost ---- string userName, string secretAnswer Register ---- ---- Register_OnPost ---- UserInformation userInformation ChangePassword ---- string resetPassword ChangePassword_OnPost ---- UserInformation userInformation UserProfile ---- ---- UserProfile_OnPost ---- ProfileInformation profileInformation GetErrorMessage ---- MembershipCreateStatus membershipCreateStatus ManageUser Admin string searchType, string searchInput DeleteUser Admin string id ManageRole Admin ---- CreateRole Admin string newRole DeleteRole Admin string id EditUser Admin string id EditUser_OnPost Admin string id, bool approved Views Module này tạo thành từ nhiều view trong đó có những views được sử dụng bởi người dùng cuối và những views được dùng để hỗ trợ việc quản trị bảo mật của module. Dưới đây là thể hiện cụ thể của các views trong module này: Liên kết tới trang login được hiển thị trên cùng phía bên phải ngay sau mục tóm lược việc mua hàng – shopping summary của mỗi trang bất kể khi nào mà người dùng là nạc danh. Sau khi người dùng đăng nhập vào hệ thống thì liên kết này sẽ được ẩn đi thay vào đó là các chức năng mà người dùng đã đăng nhập có đó là đăng xuất – logout, quản lí hồ sơ – manage profile, và thay đổi mật khẩu – change password. View đăng kí cho phép người dùng mới đăng kí tài khoản với hệ thống. Trong quá trình đăng kí người dùng mới sẽ tạo hồ sơ với các thông tin : FirstName (string) , LastName (string) , Gender (string) , BirthDate (datetime), Occupation (sring) , và website (string) . Nhóm thông tin về địa chỉ trong hồ sơ có các thuộc tính phụ trợ sau: Street, PostCode, City, State và Country tất cả đều có kiểu string. Nhóm cuối cùng về Preferences có các thuộc tính Cultrure và Newsletter . View quên mật khẩu cho phép người dùng thay đổi mật khẩu bằng việc trả lời một câu hỏi bảo mật sau khi đã điền đúng tên người dùng vào ô người dùng. Sau khi trả lời đúng câu hỏi bảo mật lúc này người dùng sẽ được phép tạo mật khẩu mới và dùng tài khoản của mình với mật khẩu mới này. Trang quản lí hồ sơ người dùng sẽ chỉ truy cập được bởi người dùng . Nó cho phép người dùng thay đổi các thông tin trên hồ sơ mà họ thiết lập từ lúc họ tạo tài khoản. Các trang để quản lí người dùng dành cho người quản trị như view dành cho quản lí người dùng ManageUser view giúp nhà quản trị tìm kiếm người dùng hoặc bằng tên hoặc bằng địa chỉ , view dành cho việc chỉnh sửa – edit user , sẽ hiển thị các chi tiế thêm về người dùng và cho phép nhà quản trị để hoặc không để tài khoản đó hoạt động , hay thay đổi cập nhật vai trò người dùng. Ngoài ra còn có view dành cho quản lí vai trò – ManageRole được sử dụng để tạo , xóa các vai trò trong hệ thống. Như vậy ta có các views của module này Tên trang Đặc tả Đường dẫn ảo ChangePassword.aspx Trang này dùng thay đổi mật khẩu của người dùng user/changepassword/ EditUser.aspx Trang này cho phép thay đổi quyền người dùng, ngăn hoạt động của tài khoản user/edituser/{id} ForgotPassword.aspx Trong trường hợp người dùng quên mật khẩu user/forgotpassword Login.aspx Trang đăng nhập user /login ManageRole.aspx Trang dành cho nhà quản trị tạo / xóa các vai trò áp dụng cho hệ thống user/managerole ManageUser.aspx Trang dành cho nhà quản trị xem danh sách người dùng, qua đó xóa người dùng user/manageruser Register.aspx Trang này cho phép người dùng nạc danh trở thành người dùng đã đăng kí bằng việc điền thông tin người dùng và đăng kí user/register UserProfile.aspx Trang hồ sơ dành cho người dùng thay đổi thông tin hồ sơ của mình user/profile Sử dụng Javascript Trong view content của trang quản lí người dùng (ManageUser.aspx) việc xóa người dùng sẽ được sử lí bằng kĩ thuật Ajax với đoạn mã javascript nằm trong tệp manage-user.js với nguồn /content/scripts/manage-user.js. Đoạn mã lệnh xóa người dùng sẽ được sử dụng để triệu gọi phương thức xóa người dùng DeleteUser trong UserController . Nội dung của đoạn lệnh: $(".delete-user-button").click(function() { var userId = $(this).attr("meta:id"); $.post( "/User/DeleteUser", { id: userId }, function(data) { $("#user-" + data.object.id).remove(); }, "json" ); return false; }); Tương tự vậy thì trong view content của trang quản lí vai trò (ManageRole.aspx) chức năng xóa vai trò cũng được xử lí bằng kĩ thuật này với đoạn mã xóa nằm trong tệp manage-role.js nguồn /content/scripts/manage-role.js. Cấu hình định tuyến Sử dụng định tuyến mặc định routes.MapRoute( "Default", "{controller}/{action}", new { controller = (string)null, action = (string)null } ); Xử lí xác thực người dùng public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args) { if (FormsAuthentication.CookiesSupported) { if (Request.Cookies[FormsAuthentication.FormsCookieName] != null) { try { FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value); args.User = new GenericPrincipal( new FormsIdentity(ticket), new string[0] // The roles are handled by the //RoleManager ); args.Context.User = args.User; } catch (Exception) { /* decryption failed */ } } } else { throw new HttpException("Cookieless Forms Authentication is not supported for this application."); } } MODULE THƯƠNG MẠI Tổng quan về module Chuỗi các siêu thị của công ty Golbal Store Pte. đều có chung cấu trúc về các gian hàng như thiết bị âm thanh – Audio, đồ cho ô tô và thiết bị định vị - Car and GPS, máy tính – Computers , điện thoại cố định và đồ văn phòng – Mobilephones and Office, âm nhạc, phim và sách truyện – Music , Movies and Books. Tuy nhiên có một số gian hàng trống tạo không gian mở cho người dùng thuê. Khách hàng khi thuê gian hàng trống phải đăng kí tài khoản , những khách hàng này có toàn quyền quản lí gian hàng của mình. Khách hàng khi mua hàng trực tuyến với Global Store Pte họ sẽ có thể duyệt các gian hàng của siêu thị - xem danh sách các gian hàng. Danh sách các gian hàng sẽ có tên gian hàng và đặc tả về gian hàng. Khi khách hàng chọn xem một gian hàng - xem danh sách các sản phầm có trong một gian hàng cụ thể . Danh sách các sản phẩm với mỗi sản phẩm sẽ có kèm theo thông tin vắn tắt về sản phẩm (tên sản phẩm, thông tin vắn tắt về sản phẩm, giá bán, giá bán giảm giá nếu có,số lượng sản phẩm còn trong cửa hàng) ,nút Add To Cart cho phép người dùng chọn đưa sản phẩm vào giỏ hàng và một liên kết cho phép xem chi tiết sản phẩm , hình ảnh sản phẩm thường là nhỏ hơn so với hình ảnh trong mục xem chi tiết. Khi khách hàng chọn xem chi tiết sản phẩm thì sẽ được chuyển đến view dành cho xem chi tiết sản phẩm. Trên view này người dùng sẽ thấy được thông tin chi tiết sản phẩm như tên sản phẩm , số hiệu sản phẩm, hình ảnh sản phẩm to hơn nếu có, thông tin chi tiết về sản phẩm nếu có, giá bán, giá bán đã có khuyến mãi giảm giá nếu có, số lượng hàng có trong kho, ô chọn số lượng mua (khách hàng hoàn toàn có thể chọn mua với số lượng mong muốn vì siêu thị có thể điều động hàng), cuối cùng là nút Add to Cart. Nếu người dùng chọn mua sản phẩm thì sẽ thấy được một hoạt cảnh (animation) dạng hình ảnh sản phẩm bay vào giỏ hàng (fly-into-cart) . Ở đây là sản phẩm sẽ bay vào vị trí có thông tin tóm tắt mua hàng (shopping summary) các thông tin vắn tắt mua hàng sẽ được cập nhật đó là số tiền phải trả và số mặt hàng trong giỏ hàng (items). Trong shopping summary cũng có liên kết check out đưa người dùng tới thông tin mua sắm chi tiết (view shoppingCart) để qua đó người dùng có thể kiểm tra chi tiết hơn những gì mình đã mua. Khách hàng khi chọn check out ở mục shopping summary sẽ được chuyển tới view thông tin chi tiết mua sắm của khác hàng. với người dùng (nhân viên , quản trị - store room worker , admin) thực hiện việc nhận lệnh đặt mua, quản lí hàng trong kho (xem, thêm, xóa , cập nhật sách), với hệ thống bên ngoài (Paypal system, shipping system) giúp thực hiện việc thanh toán trực tuyến và giao hàng tới người mua. Người dùng với vai trò quản lí siêu thị - Store Keeper khi đăng nhập vào hệ thống có thể quản lí gian hàng mà mình tạo ra như thêm mới các sản phẩm vào gian hàng, xóa sản phẩm của gian hàng, … Ghi chú: Với phần mua hàng thì không bắt buộc người dùng phải có tài khoản và đăng nhập vào hệ thống tránh trường hợp đôi khi người dùng không muốn mất thời gian đăng kí tài khoản Sơ đồ chức năng Hình 3.7 Sơ đồ chức năng theo mô hình UC của module thương mại Tìm hiểu , sử dụng Paypal cho chức năng thanh toán (check out) Paypal có 3 loại tài khoản sử dụng tiền thật cho khách hàng, những người buôn bán trực tuyến và các doanh nghiệp và 1 loại tài khoản dùng tiền ảo và dành cho các nhà phát triển ứng dụng để thử nghiệm ứng dụng của mình. Ở đây ta quan tâm tới loại hình tài khoản thứ tư : Sandbox Account vì ứng dụng chỉ ở dạng thử nghiệm. Tạo tài khoản ảo cho mục đích kiểm thử Sandbox Account: Là tài khoản có thể ở dạng dùng cho doanh nghiệp , hoặc cho người buôn bán trực tuyến hay cho cá nhân tuy nhiên lại chỉ được tạo ra và sử dụng được trong môi trường thử nghiệm và dĩ nhiên chỉ dùng cho mục đích thử nghiệm mà thôi. Để kiểm nghiệm chỉ cần 2 tài khoản : 1 doanh nghiệp , 1 cá nhân là đủ. Trước khi tạo 2 tài khoản này ta cần tạo tài khoản dành cho nhà phát triển ứng dụng (Dùng Paypal cho website thương mại của mình) sau đó với tài khoản này có thể tạo bao nhiêu tài khoản Sandbox tùy theo ý muốn. Tạo tài khoản dành cho lập trình viên tại địa chỉ sau: https://developer.paypal.com/ Sau đó tạo 2 tài khoản Sandbox dành cho người mua và bán như hình vẽ sau Hình 3.8 – Các tài khoản kiểm thử với sandbox Như vậy là ta đã có tài khoản để thực hiện các giao dịch ảo cho mục đích kiểm thử với website thương mại của mình. Quá trình thanh toán với Paypal từ website thương mại Việc tích hợp thanh toán với Paypay cho hệ thống siêu thị trực tuyến là không quá phức tạp chỉ cần tạo ra một form HTML với các tham số tương ứng và điều hướng khách hàng từ website thương mại tới trang thanh toán với paypal. " method="post"> " /> " /> " /> Checkout Đây là bảng các liệt kê các tham số cần biết để xây dựng chức năng thanh toán Thuộc tính Mô tả cmd Cho biết ta đang sử dụng trang paypal ở chế độ gì. Ví dụ như khi cmd có giá trị _xclick tức là ta đang sử dụng Paypal ở chế độ thanh toán (Pay now) . Như vậy trong hệ thống siêu thị trực tuyến này với chức năng checkout ta sẽ gán giá trị _xclick cho tham số cmd của checkout upload Với giá trị là 1 cho tham số upload cho biết ta sử dụng giỏ hàng của hệ thống chứ không dùng giỏ hàng tích hợp của Paypal business Địa chỉ thư xác định tài khoản business với Paypal của ta. Đây chính là tài khoản dùng để nhận thanh toán từ giao dịch của khách hàng. Với hệ thống tài khoản này là : dangti_1273804062_biz@yahoo.com item_name Đây là chuỗi mô tả đơn đặt hàng mà khách hàng sẽ thanh toán ví dụ như Oder #01 item_number Con số hay chuỗi số xác định cho một đơn đặt hàng amount Số tiền mà khách hàng phải trả theo tiền của một nước nào đó được chỉ định bằng mã tiền (currency_code) , định dạng tiền phải ở dạng như sau 33.88 quantity Tổng số sản phẩm đặt mua của một mặt hàng cụ thể shipping Cước vận chuyển cũng được chỉ định theo tiền của nước được chỉ định ở tham số amount với cùng định dạng tiền như vậy Các bảng dữ liệu của module Module thương mại có tất cả 5 bảng sản phẩm, bảng gian hàng (Deparments) , bảng phương thức vận chuyển (ShippingMethods), bảng đơn đặt hàng (Oders), bảng các mặt hàng trong đơn đặt hàng (OderItems) . Trong đó quan hệ giữa 2 bảng gian hàng và sản phẩm được thiết lập thông qua khóa ngoại là DepartmentID với updates và deletes là kiểu do đó khi xóa một gian hàng thì các sản phẩm có trong gian hàng cũng bị xóa. Tương tự thì có mối quan hệ giữa bảng đơn đặt hàng và bảng các mặt hàng trong đơn đặt hàng được thiết lập bởi khóa ngoại là OderID với updates và delete cũng là kiểu cascade. Hình 3.8 Sơ đồ quan hệ các bảng dữ liệu trong module thương mại Lớp thiết lập cấu hình cho module thương mại Lớp CommerceElement này dùng thiết lập các cấu hình thanh toán với Paypal được sử dụng để tạo các thuộc tính sau cho việc thanh toán Thuộc tính Mô tả PayPalServer Đây chính là máy chủ Paypal mà khách hàng gửi yêu cầu tới đó có thể là máy chủ Sandbox dành cho thử nghiệm có địa chỉ (https://www.sandbox.paypal.com/cgi-bin/webscr) hoặc là máy chủ thực (https://www.paypal.com/cgi-bin/webscr) PayPalAccount Đây là ID của PayPal của k hoặc là một địa chỉ thư điện tử tương ứng với tài khoản PayPal. Thư điện tử này cần được xác nhận lại với PayPal trước khi có thể dùng đại diện cho tài khoản đó PayPalIdentityToken Đây là thẻ token được PayPal sử dụng để xác nhận một tài khoản nào đó. Nó có thể có được thông qua site của Paypal ở mục các tùy chọn trong tài khoản PayPal Vì lí do bảo mật thì token này không được gửi tới chủ tài khoản PayPal tuy nhiên khi chủ tài khoản sử dụng Token này thì nó sẽ xuất hiện ở phía dưới các nút bật tắt kiểu radio của phần truyền dữ liệu thanh toán trên trang tùy chọn phương thức thanh toán. Model Model của module này được chia làm 2 phần . Phần thứ 1 là các lớp thực thể LINQ-to-SQL được tạo tự động từ năm bảng DL trên. Hình 3.9 – Sơ đồ các lớp thực thể LINQ-to-SQL của module Phần thứ 2 là các lớp thực thể ta xây dựng thêm hỗ trợ cho việc thực hiện một số chức năng của module ví dụ như phần chức năng với giỏ hàng. Lớp ShoppingCart được sử dụng bởi lớp trừu tượng Profile nhằm duy trì trạng thái giỏ hàng của một người dùng ngay cả khi người dùng này thoát khỏi hệ thống. Ngoài ra còn có lớp ShoppingCartItem và lớp tĩnh CommerceQueries chứa các phương thức mở rộng cho lớp Department, Products, và ShippingMethods Hình 3.10 – Các lớp xây dựng thêm hỗ trợ cho module Controller Lớp controller xử lí toàn bộ các logic nghiệp vụ ứng với các use case của module và chứa các phương thức hành động như sau Phương thức hành động Bảo mật Các tham số Index ---- ---- ViewDepartment ---- int departmentId ViewProduct ---- int productId ViewShoppingCart ---- int? shippingMethod AddShoppingCartItem ---- int productId, int? quantity DeleteShoppingCartItem ---- int id CompleteOder ---- string tx, decimal amt ShoppingSummary ---- ---- ManageStore StoreKeeper ---- ManageDepartment StoreKeeper ---- CreateDepartment StoreKeeper ---- CreateDepartment_OnPost StoreKeeper string title, string imageUrl, string description, int? important EditDepartment StoreKeeper int departmentId EditDepartment_OnPost StoreKeeper int id, string title, string imageUrl, string description, int? important DeleteDepartment StoreKeeper int id ManageProduct StoreKeeper int id CreateProduct StoreKeeper int id CreateProduct_OnPost StoreKeeper int? departmentId, string title, string description, string sku, decimal? unit Price, int? discountPercentage, int? untisInStock, string smallImageUrl, string fullImageUrl EditProduct StoreKeeper int productId EditProduct_OnPost StoreKeeper int? departmentId, string title, string description, string sku, decimal? unit Price, int? discountPercentage, int? untisInStock, string smallImageUrl, string fullImageUrl DeleteProduct StoreKeeper int id ManageOders StoreKeeper int id OderDetails StoreKeeper int id, string trackingId ManageShipping StoreKeeper int id, string trackingId CreateShipping StoreKeeper string title, decimal price DeleteShipping StoreKeeper int id View Đây là một trong số các module có nhiều view nhất trong hệ thống . Gồm có các view dành cho người dùng với vai trò StoreKeeper và các view dành cho người dùng với các vai trò còn lại. Đây là danh sách các view của module Tên trang Đặc tả Đường dẫn ảo Index.aspx Trang này hiển thị các gian hàng và đóng vai trò là cổng dành cho việc duyệt các sản phẩm store ViewDepartment.aspx Trang này được dùng để hiển thị tất cả các sản phẩm trong một gian hàng store/deparments/{deparmentId} ViewProduct.aspx Trang này dùng hiển thị một sản phẩm store/product/{productId} ViewShoppingCart.aspx Trang này ngoài việc hiển thị chi tiết việc mua hàng của khách hàng thì nó còn cung cấp một số chức năng như xóa mặt hàng, thực hiện thanh toán với PayPal store/cart CompleteOder.aspx Trang này được sử dụng để trở về hệ thống khi khách hàng đã hoàn tất việc thanh toán qua PayPal store/oder/completed TransactionError.aspx Trong trường hợp có lỗi xảy ra trong quá trình giao dịch Trang này không chứa đường dẫn ảo . Đường dẫn được định nghĩa bởi hệ thống sẽ được sử dụng để đưa ra thông báo lỗi ManageStore.aspx Trang này hiển thị tất cả các mục trong module cần được quản lí bởi storekeeper storekeeper/store ManageDepartment.aspx Trang này hiển thị tất cả các gian hàng mà storekeeper tạo ra , cho phép storekeeper này có thể xóa, hay thực hiện những thay đổi storekeeper/store/departments CreateDeparment.aspx Trang này cho phép storekeeper tạo mới gian hàng và thay đổi lại thông tin về gian hàng storekeeper/store/departments/create storekeeper/store/departments/edit/ {departmentId} ManageProducts.aspx Trang này storekeeper dùng để thêm sản phẩm mới, thay đổi lại thông tin sản phẩm hiện có storekeeper/store/products/create storekeeper/store/products/edit/ {productId} MangeOder.aspx Trang này liệt kê tất cả các đơn đặt hàng của hệ thống storekeeper/store/oders OderDetail.aspx Trang này cho phép storekeeper xem chi tiết của đơn đặt hàng storekeeper/store/oders/{oderId} ManageShipping.aspx Trang này hiển thị tất cả các tùy chọn trong phương thức vận chuyển và cho phép storekeeper thêm mới hay xóa phương thức vận chuyển storekeeper/store/shipping Sử dụng JavaScript Mã lệnh javascript xử lí các sự kiện bất đồng bộ dành cho module này nằm trong các tệp commerece.js , manage-product.js, manage-deparment.js manage-department.js được sử dụng để đảm bảo các trường trong form quản lí gian hàng có được sinh ra như yêu cầu hay không: $("#title").focus(function () { ShowMessage(this, "Enter the title for your department."); }); $("#importance").focus(function() { ShowMessage(this, "(optional) Enter the order of importance that you want the departments shown in."); }); $("#imageUrl").focus(function() { ShowMessage(this, "The relative web path of an image you want to be shown with products in this department."); }); $("#description").focus(function() { ShowMessage(this, "Enter a short description of the department to display to your users."); }); function ValidateTitle() { return VerifyRequiredField("#title", "required"); } function ValidateImageUrl() { return VerifyRequiredField("#imageUrl", "required"); } function ValidateDescription() { return VerifyRequiredField("#description", "required"); } function ValidateDepartment() { var validTitle = ValidateTitle(); var validImage = ValidateImageUrl(); var validDescription = ValidateDescription(); return validTitle && validImage && validDescription; } $("form.department-create").validate(ValidateDepartment); Tương tự thì tệp manage-product được sử dụng để đảm bảo các trường thông tin trong form tạo sản phẩm được điền vào đúng theo yêu cầu .. /************************************************************************************ Product ***********************************************************************************/ $("#title").focus(function() { ShowMessage(this, "Enter your product name here."); }); $("#sku").focus(function() { ShowMessage(this, "Enter your SKU number here."); }); $("#unitPrice").focus(function() { ShowMessage(this, "Enter the unit price of this product."); }); $("#discountPercentage").focus(function() { ShowMessage(this, "Enter the percent off you want to give for this product."); }); $("#unitsInStock").focus(function() { ShowMessage(this, "Total number of units of this product you have in stock."); }); $("#smallImageURL").focus(function() { ShowMessage(this, "The url to the thumbnail for this product."); }); $("#fullImageURL").focus(function() { ShowMessage(this, "The url to full sized image of this product."); }); function ValidateTitle() { return VerifyRequiredField("#title", "required"); } function ValidateDescription() { return VerifyRequiredField("#description", "required"); } function ValidateProduct() { return ValidateTitle() && ValidateDescription(); } $("form.product-create").validate(ValidateProduct); /*********************************************************************************** * Rich Text Editor ***********************************************************************************/ var bodyEditor; $(document).ready(function() { bodyEditor = new tinymce.Editor("description", __editorConfig); bodyEditor.onChange.add(function(ed) { bodyEditor.save(); }); bodyEditor.onClick.add(function(ed) { ShowMessage("#description", "Enter the description of your product here."); }); bodyEditor.render(); }); // clears the message from the description when another input gets focus $(":input") .focus(function() { HideMessage("#description"); }) .blur(function() { HideMessage("#description"); }); Trong tệp commerece.js có các đoạn mã lệnh cho cả quản lí gian hàng, quản lí sản phẩm ,… Đoạn code thực hiện xóa gian hàng không tạo ra việc Post back toàn bộ: $(".delete-department-button").click(function() { var departmentId = $(this).attr("meta:id"); $.post( "/Commerce/DeleteDepartment", { id: departmentId }, function(data) { $("#department-" + data.object.id).remove(); $("#admin-" + data.object.id).remove(); $("#spacer-" + data.object.id).remove(); }, "json" ); return false; }); Đoạn code thực hiện việc xóa sản phẩm khỏi gian hàng không tạo ra việc postback toàn bộ: $(".delete-product-button").click(function () { var productID = $(this).attr("meta:id"); $.post( "/Commerce/DeleteProduct", { id: productID }, function (data) { $("#product-" + data.object.id).remove(); $("#admin-" + data.object.id).remove(); $("#spacer-" + data.object.id).remove(); }, "json" ); return false; }); Đoạn code thực hiện việc các phương thức ứng với cách thức vận chuyển (như xóa cách thức vận chuyển, thêm cách thức vận chuyển ..) mà không tạo ra Post back toàn bộ: $(".delete-shipping-method-button").live("click", function() { var shippingMethodId = $(this).attr("meta:id"); $.post( "/Commerce/DeleteShipping", { id: shippingMethodId }, function(data) { $("#shipping-method-" + data.object.id).remove(); }, "json" ); return false; }); $("#add-shipping-method-button").click(function() { var title = $("#title").val(); var price = $("#price").val(); $.post( "/Commerce/CreateShipping", { title: title, price: price }, function(data) { var html = ''; html += '' + title + ''; html += '' + price + ''; html += ''; html += ''; $("#shipping-table tbody").append(html); $("#title").val(""); $("#price").val(""); }, "json" ); return false; }); Đoạn code thực hiện việc xóa sản phẩm khỏi giỏ hàng $(".delete-item-button").click(function () { var productId = $(this).attr("meta:id"); var form = $(this).parent("form"); $.post( "/Commerce/DeleteShoppingCartItem", { id: productId }, function (data) { $("#item-" + data.object.id).remove(); //Reload whole page location.reload(form); }, "json" ); return false; }); Cấu hình định tuyến Định tuyến dành cho các view của người dùng có vai trò là storekeeper: routes.MapRoute( "CommerceManageStore", "storekeeper/store", new { controller = "Commerce", action = "ManageStore" } ); routes.MapRoute( "CommerceManageDepartments", "storekeeper/store/departments", new { controller = "Commerce", action = "ManageDepartments" } ); routes.MapRoute( "CommerceCreateDepartment", "storekeeper/store/departments/create", new { controller = "Commerce", action = "CreateDepartment" } ); routes.MapRoute( "CommerceEditDepartment", "storekeeper/store/departments/edit/{departmentId}", new { controller = "Commerce", action = "EditDepartment", departmentId = (int?)null } ); routes.MapRoute( "CommerceManageProducts", "storekeeper/store/products", new { controller = "Commerce", action = "ManageProducts" } ); routes.MapRoute( "CommerceCreateProduct", "storekeeper/store/products/create", new { controller = "Commerce", action = "CreateProduct" } ); routes.MapRoute( "CommerceEditProduct", "storekeeper/store/products/edit/{productId}", new { controller = "Commerce", action = "EditProduct", productId = (int?)null } ); routes.MapRoute( "CommerceManageOrders", "storekeeper/store/orders", new { controller = "Commerce", action = "ManageOrders" } ); routes.MapRoute( "CommerceOrderDetail", "storekeeper/store/orders/{orderId}", new { controller = "Commerce", action = "OrderDetail", orderId = (int?)null } ); routes.MapRoute( "CommerceManageShipping", "storekeeper/store/shipping", new { controller = "Commerce", action = "ManageShipping" } ); Định tuyến dành cho những người dùng còn lại: routes.MapRoute( "CommerceIndex", "", new { controller = "Commerce", action = "Index" } ); routes.MapRoute( "CommerceDepartment", "store/departments/{departmentId}", new { controller = "Commerce", action = "ViewDepartment", departmentId = (int?)null } ); routes.MapRoute( "CommerceProduct", "store/products/{productId}", new { controller = "Commerce", action = "ViewProduct", productId = (int?)null } ); routes.MapRoute( "CommerceCart", "store/cart", new { controller = "Commerce", action = "ViewShoppingCart" } ); routes.MapRoute( "CommerceCompleted", "store/order/completed", new { controller = "Commerce", action = "CompleteOrder" } ); MODULE GỬI THƯ Tổng quan về module Hệ thống luôn có những nội dung cập nhật mới như bài báo , diễn đàn, thông tin sản phẩm mới … để tạo sự chú ý cho những người dùng là khách hàng thì hệ thống có module gửi thư thông báo khách hàng biết có những thông tin mới đó để rồi khách hàng sẽ quay trở lại sử dụng hệ thống trong trường hợp khách hàng đăng kí nhận thư từ hệ thống. Các vấn đề cần quan tâm khi xây dựng module: Để lưu trữ thông tin đăng kí nhận thư của người dùng ta chỉ cần lưu các thông tin như địa chỉ thư điện tử của người dùng, tên, họ, định dạng thư nhận. Tất cả các thông tin này có trong các bảng của module hồ sơ và thành viên ta hoàn toàn có thể tận dụng. Việc tạo và gửi thư điện tử của người dùng với vai trò là nhà quản trị có thể được xây dựng sử dụng namespace System.Net.Mail (namespace này được định nghĩa trong thư viện System.dll) . Các lớp chính cho việc tạo và gửi thư là lớp MailMessage, và lớp SmtpClient cung cấp các phương thức tạo và gửi thư bằng việc kết nối với máy chủ SMTP đã được cấu hình. Với lớp MailMessage ta có thể tạo toàn bộ thông tin đầy đủ của một thư điện tử như sau: // create the message MailMessage mail = new MailMessage(); // set the sender's address and display name mail.From = new MailAddress("dangtienloc@gmail.com", "Loc Dang"); // add a first recipient by specifying only her address mail.To.Add("doangquangminh@gmail.com"); // add a second recipient by specifying his address and display name mail.To.Add(new MailAddress("nguyenducdan@gmail.com", "Dan nguyen")); // add a third recipient, but to the CC field this time mail.CC.Add("mainga@gmail.com"); // set the mail's subject and HTML body mail.Subject = "Sample Mail"; mail.Body = "Hello, my friend!How are you?"; mail.IsBodyHtml = true; // set the mail’s priority to high mail.Priority = MailPriority.High; // add a couple of attachments mail.Attachments.Add( new Attachment(@"c:\demo.zip", MediaTypeNames.Application.Octet)); mail.Attachments.Add( new Attachment(@"c:\report.xls", MediaTypeNames.Application.Octet)); Khi đối tượng MailMessage đã được tạo ta có thể gửi nó bằng phương thức Send của lớp SmtpClient. SmtpClient smtpClient = new SmtpClient(); smtpClient.Send(mail); Trước khi có thể gọi phương thức Send ta cần thiết lập các thông tin cấu hình cho hệ thống để phương thức này có thể thực hiện được như địa chỉ của máy chủ SMTP (thuộc tính SmtpClient Host) , cổng (thuộc tính port) và giấy chứng thực của máy chủ này (thuộc tính credentials) , kết nối được mã hóa với SSL (thuộc tính EnableSsl) và thời gian duy trì từ lúc bắt đầu cho tới khi kết thúc của việc gửi thư ( thuộc tính TimeOut , giá trị mặc định là 100 giây) . Một thuộc tính quan trọng là DeliveryMethod (đây là thuộc tính liệt kê của kiểu dữ liệu SmtpDeliveryMethod. Nó có thể nhận một trong các giá trị sau: Network: Thư được gửi thông qua kết nối trực tiếp tới một máy chủ Smtp cụ thể PickupDirectoryFromIs: Thư điện tử này được chuẩn bị và tệp thư này được lưu vào thư mục mặc định là :\Inetpub\mailroot\Queue. IIS sẽ lấy thư này từ hàng đợi thư để gửi. SpecifedPickupDirectory: Các tệp EML với thư điện tử sẽ được gửi được lưu vào thư mục được chỉ định cho thuộc tính này của đối tượng SmtpClient. Thiết lập này sẽ hữu ích khi ta có một ứng dụng ngoài (không phải IIS) xử lí việc chọn thư và gửi Cấu hình phần gửi thư của hệ thống thương mại trực tuyến Vấn đề thời gian trong xử lí gửi thư có thể là rất lâu khiến người dùng có thể nghĩ hệ thống có lỗi. Việc gửi thư tới hàng ngàn khách hàng đăng kí nhận thư (subscribers) thường phải mất hàng phút trong khi để xử lí một yêu cầu theo tiêu chuẩn chỉ nên trong vài giây. Một giải pháp được đưa ra trong hệ thống đó là khi người dùng nhấn nút submit (tương đương với hành động gửi thư) thì ở phía máy chủ sẽ sinh ra một tiến trình xử lí nhiệm vụ gửi thư mất nhiều thời gian đó ở chế độ nền (backgroudn running mode) và chuyển hướng người dùng về trang quản lí thư . Và ở trang đó người dùng có thể thấy được trạng thái của tất cả các thư đã gửi trong đó có cả thư mới tạo gần nhất và đang được gửi . Để tránh việc phải có một nút refresh ở trang quản lí thư đó để biết được trạng thái thư mới gửi gần nhất ta sẽ sử dụng JQuery để yêu cầu dư liệu từ lớp điều khiển việc gửi thư (newsletters controller class) và rồi tạo một đối tượng DOM đưa trực tiếp dữ liệu về tình trạng xử lí thư vừa gửi vào trang quản lí thư. Đoạn code javascript đó như sau: $.ajax({ type: "GET", url: "/Newsletter/UpdateStatus", dataType: "html", sucess: function (result) { var domElement = $(result); $("#Newsletter_Status_Table").replaceWith(domElement); } }); Tạo các luồng tiến trình chạy nền – đa tiến trình Các luồng tiến trình chạy nền hay luồng phụ được sử dụng để xử lí các nhiệm vụ cần nhiều thời gian mà không ảnh hưởng tới tiến trình chính. Với ASP.NET ta có thể tạo 250 tiến trình chạy cùng một thời điểm trên một CPU Việc tạo mới một tiến trình không khó tuy nhiên ta phải quan tâm tới việc các tiến trình có thể truy cập cùng lúc tới cùng một không gian nhớ. Để tạo tiến trình ta cần namespace System.Threading trong thư viện mscorlib.dll . Dưới đây là một đoạn code tạo tiến trình phụ chạy nền // create and start a background thread with some input parameters object[] parameters = new object[]{"val1", 10, DateTime.Now}; ParameterizedThreadStart pts = new ParameterizedThreadStart(ExecuteTask); Thread thread = new Thread(pts); thread.Priority = ThreadPriority.BelowNormal; thread.Start(parameters); // main thread goes ahead immediately … // the method run asynchronously by the background thread void ExecuteTask(object data) { // extract the parameters from the input data object object[] parameters = (object[])data; string val1 = (string)parameters[0]; int val2 = (int)parameters[1]; DateTime val3 = (DateTime)parameters[2]; // execute time consuming processing here … } Sơ đồ chức năng Hình 3.11 – Sơ đồ chức năng của module Các bảng dữ liệu Hình 3.12 – Bảng Newsletters Với việc thông tin về người đăng kí nhận thư có thể sử dụng các bảng trong module hồ sơ và thành viên thì với module này chỉ cần một bảng Newsletters thể hiện nội dung thư gửi từ hệ thống, và các thông tin về thư đó. Thiết kế lớp cấu hình cho module Module này sử dụng thành phần nằm trong của tệp web.config . Để cấu hình cho module ta phải xem các thành phần cấu hình của // ở bảng sau Thuộc tính SMTP Mô tả DeliveryMethod Thiết lập thuộc tính này với “network” chỉ ra cho ứng dụng biết rằng có một mảy chủ thư điện tử bên ngoài sẽ được sử dụng để gửi thư điện tử. Để gửi được thư thì thành phần của cần được sử dụng gồm có Nơi chứa - Host,Cổng - Port,Tên người dùng - UserName,Mật khẩu- Password, và Chứng thực mặc định - DefaultCredentials. From Chỉ ra địa chỉ email sẽ được đặt trong mục “From” khi ứng dụng gửi thư đi ConfigSource Định nghĩa một nguồn ngoài cho các thiết lập cấu hình thư ví dụ như mail.config Tiếp đến là cấu hình /// Thuộc tính Network Mô tả DefaultCredentials Một giá trị kiểu bool chỉ ra rằng các chứng thực của ứng dụng có được dùng hay không. Nếu giá trị đó là true thì UserName và Password là không cần Host Tên của máy chủ thư điện tử UserName Tên người dùng được sử dụng để đăng nhập vào máy chủ thư điện tử và gửi thư. Password Mật khẩu được sử dụng để đăng nhập vào máy chủ thư điện tử để gửi thư Port Cổng sẽ được sử dụng để gửi SMTP Cuối cùng ta sẽ tạo thêm phần cấu hình cho module . Phần này thiết lập địa chỉ thư điện tử được sử dụng để gửi thư Thuộc tính Newsletters Mô tả FromEmail Địa chỉ thư điện tử thực sẽ xuất hiện trong mục “From” trong thư gửi đi ở phần đầu của thư FromDisplayName Tên hiển thị được sử dụng kèm với thuộc tính FromEmail Model Với module này ta sẽ chỉ cần model là lớp thực thể LINQ-to-SQL dưới đây: Hình 3.13 – Sơ đồ Model Newsletter Controller Với các chức năng của module như sơ đồ chức năng ta có tương ứng các phương thức hành động của controller của module Phương thức hành động Bảo mật Các tham số Index ---- ---- ManageNewsletters Editor ---- UpdateStatus Editor ---- CreateNewletters Editor string subject , string body EditNewsletters Editor int? newsletterId, string subject, string body RemoveNewsletter Editor int? newsletterId View Từ các phương thức hành động trong phần controller ta cũng có các view dành cho tương ứng . Dưới đây là danh sách các view của module Tên trang Đặc tả Đường dẫn ảo Index.aspx Trang này được sử dụng để đăng kí nhận thư từ hệ thống newsletter CreateNewsletter.aspx Trang này cho phép người dùng với vai trò là editor tạo hoặc chỉnh sửa thư editor/newsletter/create editor/newsletter/edit/{newsletterId} ManageNewsletter.aspx Trang này liệt kê toàn bộ các thư đã gửi trước đó. Editor có thể nhấn vào thư và xem lại nội dung thư hoặc xóa thư. Trang được cập nhật sau vài giây xử dụng JQuery editor/newsletter/remove/{newsletterId} editor/newsletter Cấu hình định tuyến Định tuyến cho trang index của module: routes.MapRoute( "NewsletterIndex", "newsletters", new { controller = "Newsletter", action = "Index" } ); Định tuyến cho các trang dành cho editor: routes.MapRoute( "NewsletterCreate", "editor/newsletters/create", new { controller = "Newsletter", action = "CreateNewsletter" } ); routes.MapRoute( "NewsletterEdit", "editor/newsletters/edit/{newsletterId}", new { controller = "Newsletter", action = "EditNewsletter", newsletterId = (int?)null }, new { newsletterId = "[0-9]+" } ); routes.MapRoute( "NewsletterRemove", "editor/newsletters/remove/{newsletterId}", new { controller = "Newsletter", action = "RemoveNewsletter", newsletterId = (int?)null }, new { newsletterId = "[0-9]+" } ); routes.MapRoute( "NewsletterManage", "admin/newsletters", new { controller = "Newsletter", action = "ManageNewsletters" } ); MODULE LẤY Ý KIẾN KHÁCH HÀNG Tổng quan về module Module lấy ý kiến khách hàng gồm các câu hỏi đi kèm với mỗi câu hỏi là các lựa chọn mà khách hàng có thể chọn để trả lời câu hỏi đó. Module được xây dựng vì những lí do như người quản lí hệ thống muốn nắm bắt thị hiếu khách hàng về nhiều vấn đề từ hệ thống cho đến các sản phẩm bán trong siêu thị …Ngoài ra module được xây dựng cũng đem lại cho khách hàng cảm giác họ là một phần của hệ thống và như vậy họ sẽ sử dụng hệ thống thường xuyên hơn. Về mặt chức năng thì module sẽ cho người dùng xem các chưng cầu có trong hệ thống (polls) , cho ý kiến (vote) . Với người dùng có vai trò là editor có thể quản lí các chưng cầu (chuyển chưng cầu sang trạng thái đã lấy đủ ý kiến - archive, hiện hành, chỉnh sửa chưng cầu, xóa chưng cầu) , tạo chưng cầu mới Sơ đồ chức năng Hình 3.14 – Sơ đồ chức năng của module lấy ý kiến người dùng Các bảng dữ liệu Hình 3.15 – Sơ đồ mối quan hệ giữa 2 bảng DL của module lấy ý kiến người dùng Với module này ta cần 2 bảng một bảng là bảng Polls chứa các câu hỏi lấy ý kiến và các thuộc tính của chúng như câu hỏi chưng cầu là hiện hành – current , hay đã lấy đủ ý kiến – archived… Bảng thứ 2 là bảng PollOptions chứa các thông tin về những chọn lựa cho câu hỏi đặt ra để lấy ý kiến. Mỗi một chọn lựa – options tương ứng với một câu trả lời được hệ thống tạo sẵn cho người dùng.Quan hệ giữa 2 bảng này được tạo thông qua khóa ngoại PollID Hình 3.16 – Sơ đồ thiết kế bảng PollOptions Hình 3.17 – Sơ đồ thiết kế bảng Polls Thiết kế lớp cấu hình cho module Với module này cần thiết lập một số thông tin cấu hình như thiết lập thuộc tính archived hay public cho một chưng cầu … (khi thiết lập thuộc tính public hay archived cho poll, cho phép hay không cho phép vote nhiều lần ..) Bảng dưới đây sẽ là bảng các thuộc tính thuộc lớp PollElement . Lớp này kế thừa từ lớp ConfigurationElement của framework .net mvc. Nó sẽ đọc các thiết lập của thành phần nằm trong phần thiết lập cầu hình của trong tệp web.config Thuộc tính SMTP Mô tả VotingLockInterval Một số nguyên cho biết khi nào thì cookie dành cho phần vote của một người dùng cụ thể nào đó hết hiệu lực (tương ứng với số ngày ngăn ngừa việc vote lại) VotingLockByCookie Một giá trị kiểu bool chỉ ra rằng liệu cookie có được dùng hay không được dùng cho việc nhớ người dùng đã vote hay chưa VotingLockByIP Một giá trị kiểu bool cho biết liệu địa chỉ IP của người dùng có được giữ trong bộ nhớ để tránh việc một người với cùng 1 địa chỉ IP trong phiên hiện hành vote cho cùng một poll nhiều lần ArchiveIsPublic Một giá trị kiểu bool được dùng để cho biết liệu các poll đã được thiết lập archive có được xem bởi tất cả mọi người hay không Model Model của module này cũng thiết kế theo kiểu mẫu Provider Model của Microsoft như đã trình bày trong phần lựa chọn kiến trúc hệ thống. Có 2 lớp thực thể LINQ-to-SQL được tạo tự động là: Hình 3.18 – Sơ đồ lớp PollOption và Poll Lớp tĩnh PollQueries chứa tất cả các truy vấn cần thiết để lấy thông tin từ CSDL cụ thể nó cung cấp các phương thức mở rộng cho module này. Hình 3.19 – Sơ đồ lớp PollQueries Controller Ứng với các chức năng của module ta có các phương thức hành động trong controller của module ở bảng dưới đây Phương thức hành động Bảo mật Các tham số Index ---- bool? archived, int page ViewPoll ---- int id, string path Vote ---- int optionId ManagePolls Editor int page CreatePoll Editor string question, bool? Current RemovePoll Editor int? newsletterId EditPoll Editor int pollId, string question, bool? current, bool? Archived AddOption Editor int pollId, string text RemoveOption Editor int optionId SetCurrent Editor int pollId SetArchived Editor int pollId, bool archive View Dưới đây là các trang – view của module dành cho người dùng có vai trò editor và người dùng khác của hệ thống: Tên trang Đặc tả Đường dẫn ảo Index.aspx Trang này được sử dụng để xem các poll ở các trạng thái như công khai (public) , đã lấy đủ (archived) /polls /polls/page{page} /polls?archived=true /polls/ page{page}?archived=true CreatePoll.aspx Trang này cho phép người dùng với vai trò là editor tạo hoặc chỉnh sửa poll /editor/polls/create / editor /polls/edit/{pollId} ManagePolls.aspx Trang này liệt kê toàn bộ các poll có trong hệ thống. Editor có thể thêm, chỉnh sửa, xóa các lựa chọn của poll, thiết lập poll hiện hành (set current), và xem poll hiện hành / editor /polls / editor /polls/page{page} RemovePoll.aspx Trang này dùng thực hiện việc xóa poll khỏi hệ thống /editor/polls/remove/{pollId} Sử dụng javascript Module này sử dụng 2 tệp javascript trong thư mục Content/script là tệp manage-poll.js : /****************************************************************** * Manage ******************************************************************/ $(document).ready(function() { $("#polls :input").hide("fast"); }); $(".set-current").click(function() { var id = $(this).attr("meta:id"); $.post( "/poll/setcurrent", { pollId: id }, function(data) { var poll = $("#set-current-" + data.object.pollId); poll.remove(); }, "json" ); return false; }); $(".set-archived").click(function() { var id = $(this).attr("meta:id"), archived = $(this).hasClass("archived"); $.post( "/poll/setarchived", { pollId: id, archive: !archived }, function(data) { var poll = $("#set-archived-" + data.object.pollId); poll.removeClass("archived"); if (data.object.isArchived) poll.addClass("archived"); poll.text(data.object.isArchived ? "Allow Voting" : "Archive"); }, "json" ); return false; }); /****************************************************************** * Polls ******************************************************************/ $("#question").focus(function() { ShowMessage(this, "Enter the question you would like to ask in the poll."); }); function ValidateQuestion() { return VerifyRequiredField("#question", "required"); } function ValidatePoll() { return ValidateQuestion(); } $("form.poll-create").validate(ValidatePoll); function EditOption() { var id = $(this).attr("meta:id"), option = $("#option-" + id), text = option.find(".text").text(); // hide all the childrend option.children().hide(); var optionForm = $(" Update Cancel"); // update the form optionForm.find(".update").click(function () { var id = $(this).attr("meta:id"), formText = $(this).prevAll(".edit-text").val(); $.post( "/poll/editoption", { optionId: id, text: formText }, function (data) { var comment = $("#option-" + data.object.optionId); comment.children("form").remove(); comment.children(".text").text(data.object.text); comment.children().show(); }, "json" ); }); // cancel the update optionForm.find(".cancel").click(function () { $(this).parents(".option").children(":hidden").show(); $(this).parents("form").remove(); }); // add the form to the current comment option.append(optionForm); return false; } $(".edit-option-button").click(EditOption); function DeleteOption () { var id = $(this).attr("meta:id"); $.post( "/poll/removeoption", { optionId: id }, function (data) { $("#option-" + data.object.optionId).fadeOut("slow", function () { $(this).remove() }); }, "json" ); return false; } $(".delete-option-button").click(DeleteOption); function AddOptionSuccess(data) { var optionItem = $("" + data.object.text + " Edit Delete"); optionItem.find(".edit-option-button").click(EditOption); optionItem.find(".delete-option-button").click(DeleteOption); // clear the option box $("#option").val(""); // add the new option to the other options optionItem.appendTo("#poll-options"); } $("form.poll-options-create").submit(function() { var option = $("#option").val(), pollId = $("#pollId").val(); $.post( "/poll/addoption", { pollId: pollId, text: option }, AddOptionSuccess, "json" ); return false; }); Và tệp poll.js /****************************************************************** * Poll ******************************************************************/ function RenderPoll(obj, data) { var poll = $(obj), total = data.object.total, item, percentValue, rightValue, leftValue; // clears all child nodes poll.empty(); poll.append("" + data.object.question + ""); poll.append(""); // go through each option and render it for(var i = 0; i < data.object.options.length; i++) { item = data.object.options[i]; percentValue = Math.round(item.votes / total * 100); poll.append("" + "" + item.text + "" + "" + "" + percentValue + "% (" + item.votes + " votes)" + ""); } poll.append(""); poll.append("There are " + total + " total votes for this poll."); } $(".poll form").submit(function() { var selection = $(this).find(":checked").val(); if (selection != undefined) { $.post( "/poll/vote", { optionId: selection }, function(data, textStatus) { SetCookie("poll_" + data.object.pollId, selection, 30); // render the poll for the given data RenderPoll($("#poll-" + data.object.pollId), data); }, "json" ); } return false; }); Cấu hình định tuyến Định tuyến của các trang dành cho người dùng không có vai trò editor routes.MapRoute( "PollsIndex", "polls", new { controller = "Poll", action = "Index", page = 1 } ); routes.MapRoute( "PollsIndexPaged", "polls/page{page}", new { controller = "Poll", action = "Index", page = (int?)null }, new { page = "[0-9]+" } ); Định tuyến dành của các trang dành cho người dùng có vai trò là editor routes.MapRoute( "PollCreate", "admin/polls/create", new { controller = "Poll", action = "CreatePoll" } ); routes.MapRoute( "PollEdit", "admin/polls/edit/{pollId}", new { controller = "Poll", action = "EditPoll", pollId = (int?)null }, new { pollId = "[0-9]+" } ); routes.MapRoute( "PollRemove", "admin/polls/remove/{pollId}", new { controller = "Poll", action = "RemovePoll", pollId = (int?)null }, new { pollId = "[0-9]+" } ); routes.MapRoute( "PollManager", "admin/polls", new { controller = "Poll", action = "ManagePolls", page = 1 } ); routes.MapRoute( "PollManagerPaged", "admin/polls/page{page}", new { controller = "Poll", action = "ManagePolls", page = (int?)null }, new { page = "[0-9]+" } ); MODULE DIỄN ĐÀN Tổng quan về module Là một công ty sở hữu các chuỗi siêu thị có mặt khắp nơi trên thế giới ,Global Store có rất nhiều khách hàng trung thành. Các khách hàng của Global Store có chung nhiều sở thích mà mua sắm tại các siêu thị của công ty là một trong những sở thích đó. Nhu cầu giao lưu trao đổi về những sở thích đó là hết sức tự nhiên . Nắm bắt được điểm này Global Store đã xây dựng diễn đàn tạo một sân chơi cho các khách hàng của công ty giao lưu . Diễn đàn góp phần giữa chân các khách hàng trung thành cũng như biến các khách hàng tiềm năng thành khách hàng thân thiết của công ty. Sử dụng module này người dùng nạc danh có thể duyệt xem bài bình luận trong các diễn đàn , tuy nhiên để gửi bài hay bình luận họ phải đăng nhập vào hệ thống. Chức năng quản lí diễn đàn, quản lí các bài thỏa luận dành cho người dùng có vai trò là editor. (biên tập) Sơ đồ chức năng Hình 3.20 – Sơ đồ chức năng module Forum Các bảng dữ liệu Hình 3.21 – Các bảng dữ liệu của module forum Trong module này ta cần có 3 bảng như hình vẽ. Ngoài 2 bảng Forums và Posts là các bảng cốt lõi cho một diễn đàn thì ta có thêm bảng vote dùng cho cho ý kiến một cho một post là hay hay không để người dùng có thể biết post đó được nhiều hay ít người quan tâm , thích. Xây dựng lớp ForumsElement cho thiết lập cấu hình module Các thiết lập về module Diễn đàn được định nghĩa trong thành phần trong mục của tệp web.config . Lớp này ánh xạ các thiết lập về diễn đàn ứng với các thuộc tính của nó để sử dụng cho module Thuộc tính SMTP Mô tả PostReplyPageSize Số các trả lời với mỗi một post trên một trang khi người dùng xem một post ForumPageSize Số lượng các post liệt kê ở mỗi trang khi xem một diễn đàn Model Model của module này cũng được thiết kế theo kiểu mẫu Provider Model được trình bày ở phần thiết kế kiến trúc hệ thống gồm có các lớp thực thể LINQ-to-SQL và một lớp ForumQueries chứa các phương thức mở rộng hỗ trợ truy vấn của module như hình vẽ dưới Hình 3.22 – Sơ đồ các lớp thực thể LINQ-to-SQL Post, Forum , Vote Hình 3.23 – Sơ đồ lớp ForumQueries Controller Phương thức hành động Bảo mật Các tham số Index ---- ---- ViewForum ---- int forumId, string path , int page ViewPost ---- Int postId, string path Vote ---- int post Id, int direction ManageForum Editor int? newsletterId, string subject, string body CreateForum Editor string title, string description, int? order, bool? Moderated EditForum Editor int forumId, string title, string description, int? order, bool? Moderated RemoveForum Editor int forumId, string remove CreatePost ---- int? forumId, int? parentPostId, string title, string body ClosePost Editor int postId, bool closed ApprovedPost Editor int postId, bool approved RemovePost Editor int posted View Tên trang Đặc tả Đường dẫn ảo Index.aspx Trang này dùng để xem danh sách tất cả các forums có trong hệ thống /Forum ViewForum.aspx Thông quan trang này người dùng có thể xem các post có trong một forum forums/{forumId}/{*path} forums/{forumId}/{path}/page{page} ViewPost.aspx Đây là trang cung cấp view cho một post và các reply của post đó forums/posts/{postId}/{*path} forums/posts/{postId}/{path}/page{page} CreatePost.aspx Đây là trang mà qua đó người dùng có thể tạo post mới hoặc trả lời một post nào đó forums/posts/{postId}/{*path} forums/posts/{postId}/{path}/page{page} ManageForums.aspx Đây là trang mà qua đó editor có thể quản lí diễn đàn với chức năng thêm , sửa và xóa nội dung editor/forums ManagePosts.aspx Đây là trang mà qua đó người dùng với vai trò editor có thể cho phép hay từ chối một post đang ở trong tình trạng hạn chế editor/forums/posts CreateForum.aspx Trang này cho phép editor tạo hay chỉnh sửa một diễn đàn đang tồn tại editor/forums/create editor/forums/edit/{forumId} RemoveForum.aspx Trang này dùng để hỏi liệu bạn có muốn xóa điễn đàn khỏi CSDL hay không editor/forums/remove/{forumId} Sử dụng javascript Trong module này có sử dụng 2 tệp javascript là manage-forum.js và forums.js Tệp manage-forum.js /****************************************************************** * Forum Post ******************************************************************/ $("#title").focus(function() { ShowMessage(this, "Enter the title for your forum."); }); $("#description").focus(function() { ShowMessage(this, "Enter the description of your forum."); }); $("#moderated").focus(function() { ShowMessage(this, "Do you want this forum to be moderated?"); }); $("#order").focus(function() { ShowMessage(this, "The order you want this forum to appear in, compared to the other forums."); }); function ValidateTitle() { return VerifyRequiredField("#title", "required"); } function ValidateDescription() { return VerifyRequiredField("#description", "required"); } function ValidateForum() { return ValidateTitle() && ValidateDescription(); } $("form.forum-create").validate(ValidateForum); /****************************************************************** * Editor Functions ******************************************************************/ $(".post .toggle-body").click(function() { $(this).next(".body").slideToggle("normal"); return false; }); $(".post .admin .close").click(function() { var postId = $(this).attr("meta:id"); $.post( "/forum/closepost", { postId: postId, closed: true }, function(data) { $("#post-" + data.object.postId) .fadeOut("normal", function() { var title = $(this).find("h3"); title.text(title.text() + " [closed]"); $(".admin .close", this).remove(); }) .fadeIn("normal"); }, "json" ); return false; }); $(".post .admin .approve").click(function() { var postId = $(this).attr("meta:id"); $.post( "/forum/approvepost", { postId: postId, approved: true }, function(data) { $("#post-" + data.object.postId).fadeOut("normal", function() { $(this).remove(); }); }, "json" ); return false; }); $(".post .admin .remove").click(function() { var postId = $(this).attr("meta:id"); $.post( "/forum/removepost", { postId: postId }, function(data) { $("#post-" + data.object.postId).fadeOut("normal", function() { $(this).remove(); }); }, "json" ); return false; }); $(".reply .admin .remove").click(function() { var postId = $(this).attr("meta:id"); $.post( "/forum/removepost", { postId: postId }, function(data) { $("#reply-" + data.object.postId).fadeOut("normal", function() { $(this).remove(); }); }, "json" ); return false; }); Tệp forums.js /********************************************************************************************* * Forum Post *********************************************************************************************/ $("#title").focus(function() { ShowMessage(this, "Enter the title for your post."); }); $("#body").focus(function() { ShowMessage(this, "Enter the body of your post."); }); function ValidateTitle() { return VerifyRequiredField("#title", "required"); } function ValidateBody() { return VerifyRequiredField("#body", "required"); } function ValidatePost() { return ValidateTitle() && ValidateBody(); } $("form.post-create").validate(ValidatePost); /********************************************************************************************* * Rich Text Editor *********************************************************************************************/ var bodyEditor; $(document).ready(function() { bodyEditor = new tinymce.Editor("body", __editorConfig); bodyEditor.onChange.add(function(ed) { bodyEditor.save(); }); bodyEditor.onClick.add(function(ed) { ShowMessage("#body", "Enter the body of your article."); }); bodyEditor.render(); }); // clears the message from the description when another input gets focus $(":input") .focus(function() { HideMessage("#body"); }) .blur(function() { HideMessage("#body"); }); /********************************************************************************************* * Forum *********************************************************************************************/ function VoteSuccess(data) { if (data.object.error) { alert("You must be logged in to vote."); return; } var button = $(".post .vote-" + (data.object.direction > 0 ? "up" : "down")); var number = $(".post strong"); // remove current selections and select correct button $(".post .vote-button a").removeClass("selected"); button.addClass("selected"); // set new count value number.text(data.object.voteCount); } $(".post .vote-button a").click(function() { var postId = $("#postId").val(); var href = $(this).attr("href"); var direction = (href == "#up") ? 1 : -1; $.post( "/forum/vote", { postId: postId, direction: direction }, VoteSuccess, "json" ); return false; }); Cấu hình định tuyến Định tuyến cho người dùng có vai trò editor: routes.MapRoute( "ForumCreate", "editor/forums/create", new { controller = "Forum", action = "CreateForum" } ); routes.MapRoute( "ForumEdit", "editor/forums/edit/{forumId}", new { controller = "Forum", action = "EditForum", forumId = (int?)null }, new { forumId = "[0-9]+" } ); routes.MapRoute( "ForumRemove", "editor/forums/remove/{forumId}", new { controller = "Forum", action = "RemoveForum", forumId = (int?)null }, new { forumId = "[0-9]+" } ); routes.MapRoute( "ForumPostsManager", "editor/forums/posts", new { controller = "Forum", action = "ManagePosts" } ); routes.MapRoute( "ForumManager", "editor/forums", new { controller = "Forum", action = "ManageForums" } ); Các định tuyến cho người dùng với các vai trò còn lại của hệ thống routes.MapRoute( "ForumPostCreate", "forums/{forumId}/post", new { controller = "Forum", action = "CreatePost", forumId = (int?)null }, new { forumId = "[0-9]+" } ); routes.MapRoute( "ForumPostReply", "forums/posts/{parentPostId}/reply", new { controller = "Forum", action = "CreatePost", parentPostId = (int?)null }, new { parentPostId = "[0-9]+" } ); routes.MapRoute( "ForumsIndex", "forums", new { controller = "Forum", action = "Index" } ); routes.MapRoute( "Forum", "forums/{forumId}/{*path}", new { controller = "Forum", action = "ViewForum", forumId = (int?)null, path = (string)null, page = 1 }, new { forumId = "[0-9]+" } ); routes.MapRoute( "ForumPaged", "forums/{forumId}/{path}/page{page}", new { controller = "Forum", action = "ViewForum", forumId = (int?)null, path = (string)null, page = (int?)null }, new { forumId = "[0-9]+" } ); routes.MapRoute( "ForumPost", "forums/posts/{postId}/{*path}", new { controller = "Forum", action = "ViewPost", postId = (int?)null, path = (string)null, page = 1 }, new { postId = "[0-9]+" } ); routes.MapRoute( "ForumPostPaged", "forums/posts/{postId}/{path}/page{page}", new { controller = "Forum", action = "ViewPost", postId = (int?)null, path = (string)null, page = (int?)null }, new { postId = "[0-9]+" } ); Cấu hình trong tệp web.config MODULE BÀI BÁO, TIN TỨC , VÀ BLOG Tổng quan về module Global store với mục tiêu tạo một site thương mại tương tác cao nội dung phong phú nên trong hệ thống có module cho phép người dùng viết bài xung quanh các nội dung về sự kiện nào đó tổ chức tại siêu thị, chia sẻ các hình ảnh về siêu thị ,… Người dùng sử dụng module này có thể xem toàn bộ các bài báo, xem theo đầu mục, xem chi tiết bài báo , viết lời bịnh và đánh giá . Người dùng với vai trò là editor có thêm các chức năng như quản lí các đầu mục báo, tạo đầu mục mới , xóa đầu mục đang có trong hệ thống. Quản lí các bài báo (chỉnh sửa, xóa), tạo bài báo mới Sơ đồ chức năng Hình 3.24 – Sơ đồ chức năng của module bài báo, tin tức , và blog Các bảng dữ liệu Với module này ta sẽ cần ba bảng dữ liệu là Articles (chứa các thông tin liên quan đến các bài báo), Categories (Chứa các thông tin liên quan đến đầu mục các bài báo), Comments (Chứa các thông tin liên quan đến bình luận của các bài báo) Mổi quan hệ giữa bảng Articles với bảng Comments được thiết lập thông qua khóa ngoại là ArticleID với updates và deletes là kiểu Cascade do đó khi xóa một Artile thì các Comments tương ứng với Article đó cũng bị xóa. Tương tự quan hệ giữa 2 bảng Categories và Articles được thiết lập thông qua khóa ngoại là CategoryID với updates và deletes là kiểu do đó khi xóa một Category thì các bài báo thuộc Category đó cũng bị xóa. Hình 3.25 – Sơ đồ bảng Categories, Comments, Articles và quan hệ của chúng Xây dựng lớp ArticleElement cho thiết lập cấu hình của module Các thuộc tính cấu hình trong thành phần nằm trong phần thuộc tệp web.config sẽ được đọc bởi lớp ArticleElement . Lớp này kế thừa từ namespace System.Configuration.ConfigurationElement với thuộc tính thiết lập cấu hình như trong bảng sau. Thuộc tính Mô tả PageSize Số các bài báo mặc định có trên mỗi trang. Phần cấu hình của module bài báo, tin tức, blog trong tệp web.config Model Từ các bảng DL của module trong CSDL của hệ thống ta tạo được các lớp thực thể tương ứng thông quan LINQ-to-SQL như sau: Hình 3.26 – Sơ đồ các lớp thực thể Article, Comment, Category Để hỗ trợ cho việc truy vấn các bảng dữ liệu của modu

Các file đính kèm theo tài liệu này:

  • docC272TN Tm hi7875u ASP.NET MVC v xy d7921ng siu th7883 tramp.doc