https://playframework.com
http://reactivemongo.org
代碼在: http://git.oschina.net/socialcredits/social-credits-activity-service前端
公司要作一些活動,須要一個線上的活動報名應用。想着前幾天恰好看了下 ReactiveMongo
,以爲這個小應用正好練練手。react
這個活動應用的功能很是簡單:用戶在線填寫表單,提交表單,後臺存儲數據並向指定的專員發送郵箱通知。git
整個項目目錄結構以下:數據庫
├── app │ ├── controllers │ │ └── inapi │ ├── utils │ └── views │ └── activity ├── conf ├── data │ └── src │ └── main ├── platform │ └── src │ └── main ├── project ├── static │ └── src │ └── site └── util └── src ├── main
app
、conf
都是 Play
的標準目錄,分別放置代碼文件和項目配置文件。app.views
包下的是Play的模板頁面文件。gulp
static
是用於放置前端源文件的,包括:js
、sass
等,使用gulp
編譯,並輸入到 public
目錄。api
platform
目錄放置一些業務代碼,好比:Service。sass
data
目錄是數據相關類的存放地,包括model
、domain
和數據庫訪問代碼,一此數據類相關的隱式轉換代碼也放置在此。app
util
就是工具庫了,包括常量定義、配置文件讀取、枚舉等。dom
使用 ReactiveMongo
鏈接數據庫須要先建立一個 MongoDrvier
,並調用 driver.connection
方法建立鏈接,進而經過 conn.db
方法獲取一個數據庫訪問。ide
class MyDriver private() { val driver = new MongoDriver() def connection = driver.connection(List(Settings.mongo.host)) private def db(implicit ex: ExecutionContext) = connection.db(Settings.mongo.dbName) def collActivity(implicit ex: ExecutionContext) = db.collection[BSONCollection]("activity") def collActivityRegistration(implicit ex: ExecutionContext) = db.collection[BSONCollection]("activityRegistration") }
使用 Macros.handler
是最簡單的實現 case class
與 BSON
轉換的方法,它用到了 scala macro。代碼如:implicit val __activityHandler = Macros.handler[Activity]
implicit object LocalDateTimeHandler extends BSONHandler[BSONDateTime, LocalDateTime] { override def read(bson: BSONDateTime): LocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(bson.value), ZoneOffset.ofHours(8)) override def write(t: LocalDateTime): BSONDateTime = BSONDateTime(t.toInstant(ZoneOffset.ofHours(8)).toEpochMilli) } implicit val __activityHandler = Macros.handler[Activity]
查找一個Activity使用 find()
方法獲取一個訪問數據庫遊標,再在遊標上調用 .one[Activity]
方法便可獲取一個 Activity
對象,以 Option[Activity]
def findOneById(id: BSONObjectID): Future[Option[Activity]] = { activityColl.find(BSONDocument("_id" -> id)).one[Activity] }
郵箱發送使用了 commons-email
,發送郵件的代碼很是簡單。
@Singleton class EmailService { private val emailSenderActor = Akka.system.actorOf(Props[EmailServiceActor], "email-sender") def sendEmail(id: String, subject: String, content: String): Unit = { emailSenderActor ! SendEmail(id, subject, content) } } class EmailServiceActor extends Actor with StrictLogging { override def receive: Receive = { case SendEmail(id, subject, content) => val email = new SimpleEmail() email.setHostName(Settings.email.hostName) email.setSmtpPort(Settings.email.portSsl) email.setSSLOnConnect(true) email.setAuthenticator(new DefaultAuthenticator(Settings.email.username, Settings.email.password)) email.setFrom(Settings.email.from) email.setSubject(subject) email.setMsg(content) email.addTo(Settings.email.to: _*) logger.info( s"""id: $id |from: ${Settings.email.from} |to: ${Settings.email.to} |$subject |$content""".stripMargin) val result = email.send() logger.info( s"""id: $id |[result] $result""".stripMargin) } }
程序中使用了一個 Actor
來對郵件發送動做作隊列化處理,感受有點小題大做。不過 Actor
默認郵箱是FIFO
的,這個特性很適合發送郵件的隊列操做。