<repo_name>gotoeveryone/timoney<file_sep>/app/controllers/app.go
package controllers

import (
	"timoney/app/forms"
	"timoney/app/models"

	"github.com/revel/revel"
)

// App 共通コントローラ
type App struct {
	*revel.Controller
	models.Transactional
}

// Index 初期表示
func (c App) Index() revel.Result {
	form := forms.LoginForm{}
	return c.Render(form)
}

// Login ログイン
func (c App) Login(form forms.LoginForm) revel.Result {
	revel.INFO.Println(form)
	form.Validate(c.Validation, c.Request.Locale)

	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(App.Index)
	}

	// ログイン成功
	return c.Redirect(Tradings.Index)
}
<file_sep>/app/models/app.go
package models

import "time"

// Base 基底モデル
type Base struct {
	ID uint `gorm:"primary_key" json:"id"`
}

// TimestampBase タイムスタンプ付き基底モデル
type TimestampBase struct {
	Base
	CreatedAt time.Time `gorm:"column:created;type:datetime"`
	UpdatedAt time.Time `gorm:"column:modified;type:datetime"`
}

// Account 科目
type Account struct {
	Base
	Name string `json:"name"`
}

// Trading 取引
type Trading struct {
	TimestampBase
	AccountID          int        `json:"-"`
	Account            *Account   `gorm:"ForeignKey:AccountID" json:"account"`
	Traded             *time.Time `json:"traded;type:date"`
	Name               *string    `json:"name"`
	Means              *string    `json:"means"`
	PaymentDueDate     *time.Time `json:"paymentDueDate;type:date"`
	Summary            *string    `json:"summary"`
	Suppliers          *string    `json:"suppliers"`
	Payment            int        `json:"payment"`
	DistributionRatios *int8      `json:"distributionRatio"`
}

// TradingMean 取引手段
type TradingMean struct {
	Base
	Name string `json:"name"`
}

// FavoriteTrading よく使う取引
type FavoriteTrading struct {
	TimestampBase
	Keyword            string
	AccountID          int `json:"-"`
	TradingMeanID      int `json:"-"`
	Summary            *string
	Suppliers          *string
	Payment            *int
	DistributionRatios *int8
	Created            *time.Time
	Modified           *time.Time

	Account     Account     `gorm:"ForeignKey:AccountID" json:"account"`
	TradingMean TradingMean `gorm:"ForeignKey:TradingMeanId" json:"trading"`
}
<file_sep>/app/views/Tradings/Index.html
{{set . "title" "取引一覧"}}
{{template "header.html" .}}

    <form action="{{ url "Tradings.Save" }}" method="POST">
        {{template "flash.html" .}}
        <!-- <input type="hidden" name="csrf_token" value="{{ ._csrftoken }}" /> -->
        {{ with $field := field "form.Name" .}}
        <label>名前</label><input type="text" name="{{ $field.Name }}" value="あああああ">
        {{ end }}
        {{ with $field := field "form.AccountID" .}}
        <label>勘定科目</label><input type="number" name="{{ $field.Name }}" value="1">
        {{ end }}
        {{ with $field := field "form.Traded" .}}
        <label>取引日</label><input type="text" name="{{ $field.Name }}" value="2017/7/10">
        {{ end }}
        {{ with $field := field "form.Payment" .}}
        <label>金額</label><input type="number" name="{{ $field.Name }}" value="100">
        {{ end }}
        <button>登録</button>
    </form>

    {{range .tradings}}
    <div>{{.Traded}} - {{firstof .Name "" }} - {{.Account.Name}}</div>
    {{end}}

{{template "footer.html" .}}
<file_sep>/app/controllers/trading.go
package controllers

import (
	"time"
	"timoney/app/forms"
	"timoney/app/models"

	"github.com/revel/revel"
)

// Tradings 共通コントローラ
type Tradings struct {
	*revel.Controller
	models.Transactional
}

// API 初期表示
func (c Tradings) API() revel.Result {
	tradings := []models.Trading{}
	c.Txn.Preload("Account").Find(&tradings)
	return c.RenderJSON(tradings)
}

// Index 初期表示
func (c Tradings) Index() revel.Result {
	tradings := []models.Trading{}
	c.Txn.Preload("Account").Find(&tradings)

	form := forms.TradingForm{}
	return c.Render(form, tradings)
}

// Save 保存処理
func (c Tradings) Save(form forms.TradingForm) revel.Result {
	revel.INFO.Println(form)
	// バリデーションエラー
	form.Validate(c.Validation, c.Request.Locale)
	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(Tradings.Index)
	}

	// 取引日の変換エラー
	traded, err := time.Parse("2006/1/2", form.Traded)
	if err != nil {
		c.Validation.Error("日付が不正です。").Key("tradings.form.Traded.invalid")
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(Tradings.Index)
	}

	trading := models.Trading{
		Name:      &form.Name,
		AccountID: form.AccountID,
		Traded:    &traded,
		Payment:   form.Payment,
	}

	if err := c.Txn.Create(&trading).Error; err != nil {
		return c.RenderError(err)
	}

	return c.Redirect(Tradings.Index)
}
<file_sep>/README.md
# Timoney [ティモニー] 

## 時は金なり

会計情報を管理するためのツールです。  
GolangのフレームワークであるRevelを利用しています。

## 前提

以下がインストールされていること

- Golang

## セットアップ

- Revelのコマンドを取得

```sh
$ go get -u github.com/revel/cmd/revel
```

- depの取得

```sh
$ go get -u github.com/golang/dep/cmd/dep
```

- depの実行

```sh
$ dep ensure
```

## 実行

```sh
$ revel run timoney
```
<file_sep>/app/forms/app.go
package forms

import "github.com/revel/revel"

// LoginForm ログインフォーム
type LoginForm struct {
	Account  string
	Password string
}

// Validate バリデーション
func (c LoginForm) Validate(v *revel.Validation, locale string) {
	v.Check(c.Account, revel.ValidRequired(), revel.ValidMinSize(6))
	v.Check(c.Password, revel.ValidRequired(), revel.ValidMinSize(6))
}

// TradingForm 取引入力フォーム
type TradingForm struct {
	AccountID          int
	Traded             string
	Name               string
	Means              string
	PaymentDueDate     string
	Summary            string
	Suppliers          string
	Payment            int
	DistributionRatios int8
}

// Validate バリデーション
func (c TradingForm) Validate(v *revel.Validation, locale string) {
	v.Check(c.AccountID, revel.ValidRequired())
	v.Check(c.Traded, revel.ValidRequired(), revel.ValidMinSize(8), revel.ValidMaxSize(10))
	v.Check(c.Payment, revel.ValidRequired(), revel.ValidRange(1, 9999999))
}
<file_sep>/app/models/config.go
package models

import (
	"encoding/json"
	"fmt"
	"io/ioutil"

	"github.com/revel/revel"
)

type (
	// AppConfig 設定
	AppConfig struct {
		Redis redis `json:"redis"`
		DB    db    `json:"db"`
		Mail  mail  `json:"mail"`
	}

	// Redis接続設定
	redis struct {
		Host string `json:"host"`
		Port int    `json:"port"`
		Auth string `json:"auth"`
	}

	// データベース接続設定
	db struct {
		Name     string `json:"name"`
		Host     string `json:"host"`
		Port     int    `json:"port"`
		User     string `json:"user"`
		Password string `json:"<PASSWORD>"`
	}

	// メール接続設定
	mail struct {
		SMTP      string   `json:"smtp"`
		Port      int      `json:"port"`
		User      string   `json:"user"`
		Password  string   `json:"<PASSWORD>"`
		From      string   `json:"from"`
		FromAlias string   `json:"fromAlias"`
		To        []string `json:"to"`
	}
)

// LoadConfig アプリケーション設定をJSONファイルから読み込む
func LoadConfig(config *AppConfig) {
	jsonValue, err := ioutil.ReadFile(fmt.Sprintf("%s/config.json", revel.BasePath))
	if err != nil {
		revel.ERROR.Fatalln(err)
	}
	if err := json.Unmarshal(jsonValue, &config); err != nil {
		revel.ERROR.Fatalln(err)
	}
}
<file_sep>/app/models/gorm.go
package models

import (
	"database/sql"
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	"github.com/revel/revel"
)

var (
	// データベース接続用インスタンス
	dbManager *gorm.DB
)

// InitDB テーブル初期化
func InitDB() {
	// 設定ファイル読み出し
	config := AppConfig{}
	LoadConfig(&config)
	revel.INFO.Println(config)

	var err error
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=%s",
		config.DB.User,
		config.DB.Password,
		config.DB.Host,
		config.DB.Port,
		config.DB.Name,
		"Asia%2FTokyo",
	)

	dbManager, err = gorm.Open("mysql", dsn)
	if err != nil {
		panic(err)
	}
	dbManager.LogMode(true)
	revel.INFO.Println("Connected to database")
}

// Transactional トランザクション
type Transactional struct {
	*revel.Controller
	Txn *gorm.DB
}

// Begin トランザクション開始
func (c *Transactional) Begin() revel.Result {
	revel.TRACE.Println("トランザクションを開始します。")
	tx := dbManager.Begin()
	if err := tx.Error; err != nil {
		panic(err)
	}
	c.Txn = tx
	return nil
}

// Commit トランザクションのコミット
func (c *Transactional) Commit() revel.Result {
	if c.Txn == nil {
		return nil
	}
	c.Txn.Commit()
	if err := c.Txn.Error; err != nil && err != sql.ErrTxDone {
		panic(err)
	}
	revel.TRACE.Printf("トランザクションをコミットしました。！")
	c.Txn = nil
	return nil
}

// Rollback トランザクションのロールバック
func (c *Transactional) Rollback() revel.Result {
	if c.Txn == nil {
		return nil
	}
	c.Txn.Rollback()
	if err := c.Txn.Error; err != nil && err != sql.ErrTxDone {
		panic(err)
	}
	revel.ERROR.Printf("トランザクションをロールバックしました。")
	c.Txn = nil
	return nil
}
