Skip to content

Commit

Permalink
feat: add isucari
Browse files Browse the repository at this point in the history
  • Loading branch information
isu9q-outrage-bot committed Sep 8, 2019
1 parent 2869684 commit 22f2377
Show file tree
Hide file tree
Showing 217 changed files with 28,737 additions and 0 deletions.
19 changes: 19 additions & 0 deletions webapp/README.md
@@ -0,0 +1,19 @@
# isucon9-qualify

## アプリケーションのディレクトリ構成

アプリケーションのディレクトリ構成は以下のようになっています。

```
/home/isucon/isucari/webapp/
├── docs # アプリケーションおよび外部サービスについてのドキュメント
├── frontend # フロントエンドのソースコード
├── go # Go実装
├── nodejs # Node.js実装
├── perl # Perl実装
├── php # PHP実装
├── python # Python実装
├── ruby # Ruby実装
├── public # jsやcss、画像データ等の静的ファイル
└── sql # データベースのスキーマおよび初期化に必要なSQL
```
76 changes: 76 additions & 0 deletions webapp/docs/APPLICATION_SPEC.md
@@ -0,0 +1,76 @@
# ISUCARI アプリケーション仕様書

<img src="images/logo.png" alt="ISUCARIロゴ" height="300px">

ISUCARIは椅子を売りたい人/買いたい人をつなげるフリマアプリです。
従来のECサービスと比べて以下の特徴があります。

* 安心安全の決済基盤
* 匿名配送により住所を伝えなくても取引が可能に
* 買いたい/売りたいと思った時にすぐに使えるシンプルさ

## ISUCARIの使い方

### 椅子を売ろう!

#### まずは椅子を出品しよう!

1. 椅子の情報をいれよう!
- タイムラインページの右下の出品ボタンを押すと出品画面にいくよ!
- シンプルなフォームに情報を入力すれば即出品♪
- ![1-1](images/1-1.png)
1. 売れるのを待とう!
- あなたの椅子が買われるのを楽しみに待とう♪
1. 椅子を発送しよう!
- 無事購入されたら椅子を発送しよう!
- 商品ページかマイページから取引画面に行こう👀

#### 売れた椅子を発送しよう!

1. 集荷予約をしよう!
- 集荷予約をして椅子を送る準備をしよう😤
- 集荷予約は取引画面からできるぞ!
- ![2-1](images/2-1.png)
1. 配達員に椅子をわたそう!
- 配達員が来たらQRコードを見せよう📱
- 椅子を渡したら発送完了ボタンを押そう♪
- ![2-2](images/2-2.png)
1. 購入者の受け取りを待とう!
- 椅子が届くのをまとう♪
- 届いたかどうかは取引画面で確認できるぞ!
- 購入者が椅子を受け取ったら取引完了♪

### 椅子を買おう!

1. ほしい椅子を探そう!
- タイムライン、カテゴリタイムラインから好みの椅子を探そう👀
- カテゴリタイムラインへはサイドバーからいけるよ!
- ![3-1](images/3-1.png)
1. 椅子を買おう!
- 運命の椅子を見つけたら購入しよう😎
- カード番号を入力して簡単1ステップ購入!
- ![3-2](images/3-2.png)
1. 椅子が届くのを待とう⏱
- 出品者が発送するのを待とう!
- 発送されたかどうかは取引画面で確認できるぞ!
1. 取引を完了しよう!
- 椅子が届いたら「取引完了」をしよう!
- これで取引完了♪

## キャンペーン機能について

マニュアルを参照

## 外部サービスの仕様

[外部サービス仕様書](EXTERNAL_SERVICE_SPEC.md) を参照

## ISUCARI ステータス遷移表

| | WHO | items | transaction_evidences | shippings |
|------------------------|:------:|:--------:|:---------------------:|:-------------------:|
| /sell (出品) | 出品者 | on_sale | - | - |
| /buy (購入) | 購入者 | trading | wait_shipping | initial |
| /ship (集荷予約) | 出品者 ||| wait_pickup |
| /ship_done (発送完了)| 出品者 || wait_done | shipping or done |
| /complete (取引完了) | 購入者 | sold_out | done | done |
201 changes: 201 additions & 0 deletions webapp/docs/EXTERNAL_SERVICE_SPEC.md
@@ -0,0 +1,201 @@
# 外部サービスAPIの仕様

予選マニュアルに利用可能なURLなどの情報があるので確認してください。

## payment service

決済サービスAPI。クレジットカード情報の非保持化にも対応しているので安心して利用できます。


### `POST /card`

* 加盟店ID(`shop_id`)とカード番号(`card_number`)を送ると、その加盟店IDで5分間だけ使えるトークンを発行できる
* 外部サービスからJavaScript経由でアクセスするので[Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)に対応
* カード番号の形式は`[0-9A-F]{8}`

#### API仕様

- request: application/json
- shop_id
- card_number
- response: application/json
- http status code: 200
- token
- http status code: 400
- error: json decode error
- error: wrong shop id
- error: card number is wrong

```
example:
# request
{
"shop_id": "11",
"card_number": "AAAAAAAA"
}
# response
{
"token": "abcdefg"
}
{
"error": "card number is wrong"
}
```

### `POST /token`

* 加盟店IDに紐付くAPIキー・加盟店IDに紐付くトークン・値段を送ると実際に決済が行われる
* 残高不足などの理由で正当なカード番号でも決済に失敗するケースがある

#### API仕様

- request: application/json
- shop_id
- token
- api_key
- price
- response: application/json
- http status code: 200
- status: ok
- 決済成功
- status: fail
- 決済失敗
- status: invalid
- 無効なトークン
- http status code: 400
- error: json decode error
- error: wrong shop id
- error: wrong api key

```
example:
# request
{
"shop_id": "11",
"token": "abcd",
"api_key": "itisapikey",
"price": 10000
}
# response
{
"status": "ok"
}
{
"error": "wrong shop id"
}
```

## shipment service

配送サービスAPI。配送会社が直接住所を扱うことで、お客様同士は住所を教え合うことなく利用できます。

### Authorization

AuthorizationヘッダのBearerトークンに、予め払い出されているユニークなappidを使用して認証を行う。

```
Authorization: Bearer <APP_ID>
```

### `POST /create`

* 集荷予約の作成
* 配送先の住所・配送元の住所・名前を送ると数字10桁のid(集荷予約ID)が送られてくる

#### API仕様

- request: application/json
- to_address
- to_name
- from_address
- from_name
- response: application/json
- http status code: 200
- reserve_id: 0000000000
- reserve_time: 1570000000
- (epoch time)
- http status code: 400
- error: json decode error
- error: required parameter was not passed
- http status code: 401
- (Authorization失敗)

### `POST /request`

* 集荷リクエスト
* 集荷予約IDを送ると集荷リクエストができる
* レスポンスは `/accept` へのURLが書かれているQRコードの画像ファイル
* サービス側は画像を保存して、自前で配信する必要がある

#### API仕様

- request: application/json
- reserve_id
- response: image/png (error時はapplication/json)
- http status code: 200
- png binary
- http status code: 400
- error: json decode error
- error: required parameter was not passed
- error: empty
- http status code: 401
- (Authorization失敗)

### `GET /accept`

* 発送
* 集荷予約IDとトークンを一緒にリクエストをする必要がある
* QRコードのURLに正しいトークンが付与されている
* 配達員が開く想定なので、配達員しか知らない認証を入れるべきだが、ISUCON的に厳しそうなので現在は認証なし

#### API仕様

- request: application/json
- reserve_id
- response: application/json
- http status code: 200
- accept: ok
- http status code: 400
- error: wrong parameters
- error: empty

### `GET /status`

* 配送ステータス
* 集荷予約IDを送ると `initial`, `wait_pickup`, `shipping`, `done` のどれかのステータスが返ってくる

#### API仕様

- request: application/json
- reserve_id
- response: application/json
- http status code: 200
- reserve_time: 1570000000
- (epoch time)
- status: done
- 下記参照
- http status code: 400
- error: json decode error
- error: required parameter was not passed
- error: empty
- http status code: 401
- (Authorization失敗)

### ステータスの仕様

ステータスは以下

1. `initial`: 集荷予約作成
* `/create` を呼ばれた後はこの状態
2. `wait_pickup`: 集荷待ち
* `/request` を呼ばれた後はこの状態
3. `shipping`: 配送中
* `/accept` を呼ばれた後はこの状態
4. `done`: 配送完了
* 配送が終了するとこのステータスになる
Binary file added webapp/docs/images/1-1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/docs/images/2-1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/docs/images/2-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/docs/images/3-1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/docs/images/3-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/docs/images/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions webapp/frontend/.gitignore
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# build
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
8 changes: 8 additions & 0 deletions webapp/frontend/.prettierrc.js
@@ -0,0 +1,8 @@
module.exports = {
semi : true,
singleQuote : true,
tabWidth : 2,
trailingComma : 'all',
parser : 'typescript',
filepath : './src/**/*.{ts,tsx}',
};
2 changes: 2 additions & 0 deletions webapp/frontend/.storybook/addons.js
@@ -0,0 +1,2 @@
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
19 changes: 19 additions & 0 deletions webapp/frontend/.storybook/config.tsx
@@ -0,0 +1,19 @@
import {addDecorator, configure} from '@storybook/react';
import {MuiThemeProvider} from "@material-ui/core";
import {themeInstance} from "../src/theme";
import * as React from "react";

// automatically import all files ending in *.stories.tsx
const req = require.context('../src', true, /\.stories\.tsx$/);
function loadStories() {
req.keys().forEach(req);
}

const BaseDecorator = storyFn => (
<MuiThemeProvider theme={themeInstance}>
{storyFn()}
</MuiThemeProvider>
);
addDecorator(BaseDecorator);

configure(loadStories, module);
11 changes: 11 additions & 0 deletions webapp/frontend/.storybook/webpack.config.js
@@ -0,0 +1,11 @@
module.exports = ({ config, mode }) => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [['react-app', { flow: false, typescript: true }]],
},
});
config.resolve.extensions.push('.ts', '.tsx');
return config;
};
28 changes: 28 additions & 0 deletions webapp/frontend/README.md
@@ -0,0 +1,28 @@
# ISUCON9 qualify frontend

ISUCON9予選のフロントエンドソースコードです

## init

```sh
npm i
```

## scripts

### `npm run deploy`

[../public](../public)ディレクトリにビルドしたファイルを配置します。

**`upload`ディレクトリを除く全てのファイルを上書きするので注意してください**

### `npm run storybook`

Storybookを起動します

# Other

- ロゴ作成にはHatchfulを利用しました
- https://hatchful.shopify.com/
- UIライブラリにMaterial-UIを利用しました
- https://material-ui.com/

0 comments on commit 22f2377

Please sign in to comment.