Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
joymufeng committed Jul 5, 2017
1 parent b0a4d97 commit bc3b000
Show file tree
Hide file tree
Showing 160 changed files with 3,413 additions and 19 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
logs
target
/.idea
/.idea_modules
/.classpath
/.project
/.settings
/RUNNING_PID
24 changes: 5 additions & 19 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
MIT License
License
-------
Written in 2016 by Lightbend <info@lightbend.com>

Copyright (c) 2017 mufeng
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[<img src="https://img.shields.io/travis/playframework/play-scala-starter-example.svg"/>](https://travis-ci.org/playframework/play-scala-starter-example)

# Play Scala Starter

This is a starter application that shows how Play works. Please see the documentation at https://www.playframework.com/documentation/latest/Home for more details.

## Running

Run this using [sbt](http://www.scala-sbt.org/). If you downloaded this project from http://www.playframework.com/download then you'll find a prepackaged version of sbt in the project directory:

```
sbt run
```

And then go to http://localhost:9000 to see the running web application.

There are several demonstration files available in this template.

## Controllers

- HomeController.scala:

Shows how to handle simple HTTP requests.

- AsyncController.scala:

Shows how to do asynchronous programming when handling a request.

- CountController.scala:

Shows how to inject a component into a controller and use the component when
handling requests.

## Components

- Module.scala:

Shows how to use Guice to bind all the components needed by your application.

- Counter.scala:

An example of a component that contains state, in this case a simple counter.

- ApplicationTimer.scala:

An example of a component that starts when the application starts and stops
when the application stops.

## Filters

- Filters.scala:

Creates the list of HTTP filters used by your application.

- ExampleFilter.scala

A simple filter that adds a header to every response.
21 changes: 21 additions & 0 deletions app/Module.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import com.google.inject.AbstractModule
import java.time.Clock

/**
* This class is a Guice module that tells Guice how to bind several
* different types. This Guice module is created when the Play
* application starts.
* Play will automatically use any class called `Module` that is in
* the root package. You can create modules in other locations by
* adding `play.modules.enabled` settings to the `application.conf`
* configuration file.
*/
class Module extends AbstractModule {

override def configure() = {
// Use the system clock as the default implementation of Clock
bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)
}

}
62 changes: 62 additions & 0 deletions app/controllers/ArticleController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package controllers

import javax.inject._

import models.{Document, Reply}
import play.api.libs.json.{JsObject, JsValue, Json, Writes}
import play.api.mvc._
import play.modules.reactivemongo.{MongoController, ReactiveMongoApi, ReactiveMongoComponents}
import reactivemongo.play.json.collection.JSONCollection
import utils.{HashUtil, RoaringBitmapUtil}
import reactivemongo.play.json._
import collection._
import reactivemongo.play.json.BSONDateTimeFormat
import models.JsonFormats._
import org.joda.time.{DateTime, DateTimeZone}
import org.roaringbitmap.RoaringBitmap
import play.api.libs.concurrent.Execution.Implicits.defaultContext

@Singleton
class ArticleController @Inject()(val reactiveMongoApi: ReactiveMongoApi) extends Controller {
def articleColFuture = reactiveMongoApi.database.map(_.collection[JSONCollection]("common-article"))

def index() = Action { implicit request: Request[AnyContent] =>
Ok(views.html.index())
}

def add = Action { implicit request: Request[AnyContent] =>
Ok(views.html.article.add())
}

def vote(articleId: String, up: Boolean) = Action.async { implicit request: Request[AnyContent] =>

val login = "joymufeng1"
(for {
articleCol <- articleColFuture
objOpt <- articleCol.find(Json.obj("_id" -> articleId)).one[JsObject]
} yield {
//articleCol.insert(Article("0", "", "", "", "", "", "", "", "", List.empty[String], DateTime.now(DateTimeZone.UTC), DateTime.now(DateTimeZone.UTC), List.empty[Reply], 0, List.empty[Long], 0, List.empty[Long], 0)).foreach(println _)

objOpt.fold(Ok(Json.obj("success" -> false))){ obj =>
val bitmapStr = (obj \ "viewBitMap").as[String].trim
val bitmap = RoaringBitmapUtil.fromBase64String(bitmapStr)
val userHash = HashUtil.toInt(login)
println(bitmap.contains(userHash))
if (!bitmap.contains(userHash)) {
println("add user")
bitmap.add(userHash)
articleCol.update(Json.obj("_id" -> articleId), Json.obj("$inc" -> Json.obj("voteCount" -> 1), "$set" -> Json.obj("viewBitMap" -> RoaringBitmapUtil.toBase64String(bitmap))))
} else {
println("Already voted.")
}

Ok(Json.obj("success" -> false))
}

}).recover{ case t: Throwable =>
println(t.getMessage)
t.printStackTrace()
Ok("error")
}
}
}
28 changes: 28 additions & 0 deletions app/controllers/HomeController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package controllers

import javax.inject._

import play.api._
import play.api.mvc._
import play.modules.reactivemongo.{MongoController, ReactiveMongoApi, ReactiveMongoComponents}
import reactivemongo.play.json.collection.JSONCollection
import play.api.libs.concurrent.Execution.Implicits.defaultContext

@Singleton
class HomeController @Inject()(val reactiveMongoApi: ReactiveMongoApi) extends Controller {
def robotColFuture = reactiveMongoApi.database.map(_.collection[JSONCollection]("common-robot"))

def index() = Action { implicit request: Request[AnyContent] =>
Ok(views.html.index())
}

def login(login: Option[String]) = Action { implicit request: Request[AnyContent] =>
Ok(views.html.login())
}

def doLogin = Action { implicit request: Request[AnyContent] =>
val login = request.body.asFormUrlEncoded.get.get("login").getOrElse(List(""))(0)
val password = request.body.asFormUrlEncoded.get.get("password").getOrElse(List(""))(0)
Redirect(routes.HomeController.index()).withSession("login" -> login, "name" -> login)
}
}
65 changes: 65 additions & 0 deletions app/models/Common.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package models

import org.joda.time.DateTime

case class User(_id: Int, login: String, password: String, name: String, gender: String, introduction: String, headImg: String, ip: String, createTime: DateTime, updateTime: DateTime, lastLoginTime: DateTime, enabled: Boolean)
case class Category(_id: String, name: String, path: String, parentPath: String, disabled: Boolean)

// 已整理文档
case class Document(
_id: String,
title: String,
content: String,
editorType: String,
author: Author,
categoryPath: String,
tags: List[String],
createTime: DateTime,
updateTime: DateTime,
replies: List[Reply],
viewStat: ViewStat,
voteStat: VoteStat,
index: Int // 显示排序
)

// 问答
case class Question(
_id: String,
title: String,
content: String,
editorType: String,
author: Author,
categoryPath: String,
tags: List[String],
createTime: DateTime,
updateTime: DateTime,
answer: Option[Reply],
replies: List[Reply],
viewStat: ViewStat,
voteStat: VoteStat
)

// 分享
case class Article(
_id: String,
title: String,
content: String,
editorType: String,
author: Author,
categoryPath: String,
tags: List[String],
createTime: DateTime,
updateTime: DateTime,
replies: List[Reply],
viewStat: ViewStat,
voteStat: VoteStat,
top: Boolean, // 置顶
recommended: Boolean // 精华
)

case class Author(_id: String, login: String, name: String, headImg: String)
case class ViewStat(viewCount: Int, viewBitMap: String)
case class VoteStat(voteCount: Int, voteBitMap: String)
case class Reply(_id: String, content: String, editorType: String, replier: Author, replyTime: DateTime, viewStat: ViewStat, comments: List[Comment])
case class Comment(_id: String, content: String, editorType: String, commentator: Author, commentTime: DateTime)

19 changes: 19 additions & 0 deletions app/models/JsonFormats.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package models

import play.api.libs.json.Json

/**
* Created by Le'novo on 2017/7/2.
*/
object JsonFormats {

implicit val viewStatFormat = Json.format[ViewStat]
implicit val voteStatFormat = Json.format[VoteStat]
implicit val authorFormat = Json.format[Author]
implicit val commentFormat = Json.format[Comment]
implicit val replyFormat = Json.format[Reply]
implicit val documentFormat = Json.format[Document]
implicit val articleFormat = Json.format[Article]
implicit val questionFormat = Json.format[Question]

}
27 changes: 27 additions & 0 deletions app/service/CounterService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package service

import javax.inject.{Inject, Singleton}
import play.api.libs.json.{JsObject, Json}
import play.modules.reactivemongo.ReactiveMongoApi
import reactivemongo.play.json.collection.JSONCollection
import scala.concurrent.Future
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import reactivemongo.play.json._

@Singleton
class CounterService @Inject()(val reactiveMongoApi: ReactiveMongoApi) {
def articleColFuture = reactiveMongoApi.database.map(_.collection[JSONCollection]("common-counter"))

def getNextSequence(name: String): Future[Int] = {
articleColFuture.flatMap{ articleCol =>
articleCol.findAndModify(
Json.obj("_id" -> "user-sequence"),
articleCol.updateModifier(Json.obj("$inc" -> Json.obj("value" -> 1)), true, true)
).map(_.result[JsObject]).map{
case Some(obj) => (obj \ "value").as[Int]
case None => 0
}
}
}

}
11 changes: 11 additions & 0 deletions app/utils/HashUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package utils;

/**
* Created by Le'novo on 2017/7/2.
*/
public class HashUtil {
public static int toInt(String s) {
//return s.hashCode() & 0x7fffffff;
return s.hashCode();
}
}
40 changes: 40 additions & 0 deletions app/utils/RoaringBitmapUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package utils;

import org.roaringbitmap.RoaringBitmap;
import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Base64;

/**
* Created by Le'novo on 2017/7/3.
*/
public class RoaringBitmapUtil {
public static String toBase64String(RoaringBitmap bitmap) throws IOException {
bitmap.runOptimize();
ByteBuffer buffer = ByteBuffer.allocate(bitmap.serializedSizeInBytes());
bitmap.serialize(new DataOutputStream(new OutputStream(){
ByteBuffer mBB;
OutputStream init(ByteBuffer mbb) { mBB = mbb; return this; }
public void close() {}
public void flush() {}
public void write(int b) { mBB.put((byte) b); }
public void write(byte[] b) { mBB.put(b); }
public void write(byte[] b, int off, int l) { mBB.put(b,off,l); }
}.init(buffer)));

buffer.flip();
return Base64.getEncoder().encodeToString(buffer.array());
}

public static RoaringBitmap fromBase64String(String base64) {
if (base64 != null && !base64.trim().equals("")) {
ByteBuffer buffer = ByteBuffer.wrap(Base64.getDecoder().decode(base64.trim()));
return new RoaringBitmap(new ImmutableRoaringBitmap(buffer));
} else {
return new RoaringBitmap();
}
}
}
Loading

0 comments on commit bc3b000

Please sign in to comment.