FIX: 完善登录接口1.1

master
陈古峰 3 years ago
parent fa92abf1ee
commit 123df86804

@ -82,6 +82,7 @@
<orderEntry type="library" scope="PROVIDED" name="rb-fsevent (v0.11.2, Remote: ruby-2.3.7-p456) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="rb-inotify (v0.10.1, Remote: ruby-2.3.7-p456) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="regexp_parser (v1.8.2, Remote: ruby-2.3.7-p456) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="request_store (v1.5.1, Remote: ruby-2.3.7-p456) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="ruby_dep (v1.5.0, Remote: ruby-2.3.7-p456) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="rubyzip (v1.3.0, Remote: ruby-2.3.7-p456) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="sass (v3.7.4, Remote: ruby-2.3.7-p456) [gem]" level="application" />

@ -24,7 +24,7 @@ gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
gem 'redis'
gem 'redis-rails'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
@ -36,7 +36,7 @@ gem 'redis'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false
gem 'request_store'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

@ -139,7 +139,25 @@ GEM
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.4.0)
redis-actionpack (5.3.0)
actionpack (>= 5, < 8)
redis-rack (>= 2.1.0, < 3)
redis-store (>= 1.1.0, < 2)
redis-activesupport (5.3.0)
activesupport (>= 3, < 8)
redis-store (>= 1.3, < 2)
redis-rack (2.1.4)
rack (>= 2.0.8, < 3)
redis-store (>= 1.2, < 2)
redis-rails (5.0.2)
redis-actionpack (>= 5.0, < 6)
redis-activesupport (>= 5.0, < 6)
redis-store (>= 1.2, < 2)
redis-store (1.9.2)
redis (>= 4, < 6)
regexp_parser (1.8.2)
request_store (1.5.1)
rack (>= 1.4)
ruby_dep (1.5.0)
rubyzip (1.3.0)
sass (3.7.4)
@ -205,7 +223,8 @@ DEPENDENCIES
mysql2
puma (~> 3.11)
rails (~> 5.2.8, >= 5.2.8.1)
redis
redis-rails
request_store
sass-rails (~> 5.0)
selenium-webdriver
spring

@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

@ -0,0 +1,3 @@
// Place all the styles related to the Tokens controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -0,0 +1,3 @@
// Place all the styles related to the UserActions controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AccountsController < ApplicationController
include LoginHelper
def login
user_try_to_login = User.try_to_login(params[:login])
return info(-1, "用户账号不存在") if user_try_to_login.blank?
@ -21,6 +21,10 @@ class AccountsController < ApplicationController
login_control.increment!
return
end
successful_authentication(user_try_to_login)
# self.logged_user = user_try_to_login
login_control.clear
info(0, "登录成功")
end
@ -59,4 +63,43 @@ class AccountsController < ApplicationController
end
end
end
def successful_authentication(user)
ActiveRecord::Base.transaction do
self.logged_user = user
set_token_session user
set_autologin_cookie(user)
UserAction.create(action_id: user.try(:id),
action_type: "Login",
user_id: user.try(:id),
ip: request.remote_ip)
user.update_column(:last_login_on, Time.now)
session[:user_id] = user.id
response.header['cs'] = "#{session&.id&.to_s}"
response.header['Access-Control-Expose-Headers'] = "Cs,Set-Cookie"
end
end
def logged_user=(user)
session_id = session.id.to_s || session[:session_id]
reset_session
if user && user.is_a?(User)
User.current = user
start_user_session(user)
else
User.current = nil
end
end
def start_user_session(user)
UserOnline.login(user.id)
session[:request_user_id] = user.id
session[:user_id] = user.id
session[:ctime] = Time.now.utc.to_i
session[:atime] = Time.now.utc.to_i
end
end

@ -0,0 +1,29 @@
# frozen_string_literal: true
module LoginHelper
extend ActiveSupport::Concern
def edu_setting(name)
EduSetting.get(name)
end
def autologin_cookie_name
edu_setting('autologin_cookie_name').presence || 'autologin'
end
def set_token_session(user)
Token.create(:value => (session.id.to_s || session[:session_id]),:user => user, :action => User::SESSION_ACTION, :ip => request.remote_ip, :browser => (request&.headers["User-Agent"] || ''))
end
def set_autologin_cookie(user)
token = Token.get_or_create_permanent_login_token(user, "autologin")
cookie_options = {
:value => token.value,
:expires => 1.month.from_now,
:path => '/',
:secure => false,
:httponly => false
}
if edu_setting('cookie_domain').present?
cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain'))
end
cookies[autologin_cookie_name] = cookie_options
end
end

@ -0,0 +1,70 @@
class TokensController < ApplicationController
before_action :set_token, only: %i[ show edit update destroy ]
# GET /tokens or /tokens.json
def index
@tokens = Token.all
end
# GET /tokens/1 or /tokens/1.json
def show
end
# GET /tokens/new
def new
@token = Token.new
end
# GET /tokens/1/edit
def edit
end
# POST /tokens or /tokens.json
def create
@token = Token.new(token_params)
respond_to do |format|
if @token.save
format.html { redirect_to token_url(@token), notice: "Token was successfully created." }
format.json { render :show, status: :created, location: @token }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @token.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /tokens/1 or /tokens/1.json
def update
respond_to do |format|
if @token.update(token_params)
format.html { redirect_to token_url(@token), notice: "Token was successfully updated." }
format.json { render :show, status: :ok, location: @token }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @token.errors, status: :unprocessable_entity }
end
end
end
# DELETE /tokens/1 or /tokens/1.json
def destroy
@token.destroy
respond_to do |format|
format.html { redirect_to tokens_url, notice: "Token was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_token
@token = Token.find(params[:id])
end
# Only allow a list of trusted parameters through.
def token_params
params.require(:token).permit(:user_id, :action, :status, :value, :ip, :browser, :created_on)
end
end

@ -0,0 +1,70 @@
class UserActionsController < ApplicationController
before_action :set_user_action, only: %i[ show edit update destroy ]
# GET /user_actions or /user_actions.json
def index
@user_actions = UserAction.all
end
# GET /user_actions/1 or /user_actions/1.json
def show
end
# GET /user_actions/new
def new
@user_action = UserAction.new
end
# GET /user_actions/1/edit
def edit
end
# POST /user_actions or /user_actions.json
def create
@user_action = UserAction.new(user_action_params)
respond_to do |format|
if @user_action.save
format.html { redirect_to user_action_url(@user_action), notice: "User action was successfully created." }
format.json { render :show, status: :created, location: @user_action }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @user_action.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /user_actions/1 or /user_actions/1.json
def update
respond_to do |format|
if @user_action.update(user_action_params)
format.html { redirect_to user_action_url(@user_action), notice: "User action was successfully updated." }
format.json { render :show, status: :ok, location: @user_action }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @user_action.errors, status: :unprocessable_entity }
end
end
end
# DELETE /user_actions/1 or /user_actions/1.json
def destroy
@user_action.destroy
respond_to do |format|
format.html { redirect_to user_actions_url, notice: "User action was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user_action
@user_action = UserAction.find(params[:id])
end
# Only allow a list of trusted parameters through.
def user_action_params
params.require(:user_action).permit(:user_id, :action_type, :action_id, :ip)
end
end

@ -0,0 +1,2 @@
module TokensHelper
end

@ -0,0 +1,2 @@
module UserActionsHelper
end

@ -0,0 +1,73 @@
module UserOnline
class << self
def login(user_id)
set_bit(user_id, 1)
end
def logout(user_id)
set_bit(user_id, 0)
end
def set_bit(user_id, flag)
if Rails.cache.data.ttl(cache_key) == -2
# 如果key不存在
set_bit_and_expire(cache_key, user_id, flag)
else
if Rails.cache.data.ttl(cache_key) ==-1
# 可以存在,但过期时间没关联
# 这种情况大概率是并发原因导致对相应key设置过期时跳过了。所以删除并重新设置过期时间
Rails.cache.data.del(cache_key)
set_bit_and_expire(cache_key, user_id, flag)
end
# 统计当前时间下前5分钟的活跃人数
Rails.cache.data.setbit(cache_key, user_id, flag)
end
end
def set_bit_and_expire(cache_key, user_id, flag)
Rails.cache.data.setbit(cache_key, user_id, flag)
# Rails.cache.data.setbit(last_cache_key, user_id, flag)
# 现在统计5分钟在线人数这里设置6分钟过期目的是统计当前时刻的前5分钟
Rails.cache.data.expire(cache_key, 6 * 60)
end
def count
if Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
now = Time.now()
keys = []
# 统计当前时间下前5分钟的活跃人数
(1..5).each do |i|
key = (now - i.minutes).strftime("%H-%M")
keys << "cache:online_user_#{key}"
end
# 防止每个分钟数据有重复,所以去重去除
Rails.cache.data.bitop("OR", "daiao", keys)
Rails.cache.data.bitcount("daiao")
else
0
end
end
def cache_key
if Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
"cache:online_user_#{Time.now().strftime("%H-%M")}"
else
raise '请配置config.cache_store = redis_store'
end
end
def last_cache_key
if Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
# 如设置5分钟内即300秒有请求的用户视为在线, 则最大会统计到10分钟内有活动用户
begin_hour = Time.now.beginning_of_hour
minutes_piece = (Time.now - begin_hour) / (60 * 5)
time = begin_hour.since((minutes_piece.to_i - 1) * (60 * 5)).strftime("%H-%M")
"cache:online_user_#{time}"
else
raise '请配置config.cache_store = redis_store'
end
end
end
end

@ -0,0 +1,124 @@
class Token < ActiveRecord::Base
include ApplicationHelper
belongs_to :user
validates_uniqueness_of :value
before_create :delete_previous_tokens, :generate_new_token
@@validity_time = 1.day
STATUS_LOGIN = 0
STATUS_LOGOUT = 1
STATUS_EX_LOGIN = 2
STATUS_EX_LOGOUT = 3
STATUS_SELF_LOGOUT = 4
STATUS_EX_OUT_LOGOUT = 5
# status 0=正常登陆1=正常退出登陆失效2=考试期间输入考试密码登录3=考试期间被挤出登录失效4=同一个浏览器登录导致失效5考试期间进入考试导致其他登录用户失效
def generate_new_token
self.value = Token.generate_token_value if action != User::SESSION_ACTION
end
def self.get_or_create_permanent_login_token(user, type)
token = Token.get_token_from_user(user, type)
unless token
token = Token.create(:user => user, :action => type)
else
token.update_attribute(:created_on, Time.now)
end
token
end
# 更新session的token的状态值
def self.update_token_by_session(user_id,session_id = nil,status = self::STATUS_SELF_LOGOUT)
if session_id.present?
token = Token.find_by(:action => User::SESSION_ACTION, :user_id => user_id, :value => session_id)
if token.present?
token.status = status
token.save!
end
else # 同一把所有的token记录状态修改掉并清空对应session_id的缓存
self.delete_user_session(user_id,nil,status)
end
end
# 删除用户的session记录
def self.delete_user_session(user_id,now_session_id = nil,status = self::STATUS_SELF_LOGOUT)
session_store = Rails.application.config.session_store.new(:cache_store, Rails.application.config.session_options)
tokens = Token.where(:action => User::SESSION_ACTION, :user_id => user_id,status:[self::STATUS_LOGIN,self::STATUS_EX_LOGIN])
tokens = tokens.where.not(value:now_session_id) if now_session_id.present?
tokens.each do |token|
token.status = status
if token.save! # 开始清空session缓存
rr = session_store.delete_session(ENV, token.value,Rails.application.config.session_options)
Rails.logger.info("调试删除session===#{token.value}===#{rr}")
end
end
end
def self.get_token_from_user(user, action)
token = Token.where(:action => action, :user_id => user).first
unless token
token = Token.create!(user_id: user.id, action: action)
end
token
end
# Return true if token has expired
def expired?
return Time.now > self.created_on + @@validity_time
end
# Delete all expired tokens
def self.destroy_expired
Token.delete_all ["action NOT IN (?) AND created_on < ?", ['feeds', 'api', 'autologin'], Time.now - @@validity_time]
end
# Returns the active user who owns the key for the given action
def self.find_active_user(action, key, validity_days=nil)
user = find_user(action, key, validity_days)
if user && user.active?
user
end
end
# Returns the user who owns the key for the given action
def self.find_user(action, key, validity_days=nil)
token = find_token(action, key, validity_days)
if token
token.user
end
end
# Returns the token for action and key with an optional
# validity duration (in number of days)
def self.find_token(action, key, validity_days=nil)
action = action.to_s
key = key.to_s
return nil unless action.present? && key =~ /\A[a-z0-9]+\z/i
# if action == User::SESSION_ACTION # 如果是session的则需要判断token是否有效
# token = Token.where(value: key, action: action).first
# else
# token = Token.where(value: key, action: action).first
# end
token = Token.where(value: key, action: action).first
if token && (token.action == action) && (token.value == key) && token.user
if validity_days.nil? || (token.created_on > validity_days.days.ago)
token
end
end
end
def self.generate_token_value
Edu::Utils.random_hex(20)
end
def self.delete_user_all_tokens(user)
Token.delete_all(user_id: user.id)
end
private
# Removes obsolete tokens (same user and action)
def delete_previous_tokens
if user && action!='_educoder_session'
Token.where(['user_id = ? AND action = ?', user.id, action]).delete_all
end
end
end

@ -7,7 +7,7 @@ class User < ApplicationRecord
VALID_PHONE_REGEX = /\A1\d{10}\z/
# 身份证验证
VALID_NUMBER_REGEX = /(\A[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^([A-Z]\d{6,10}(\(\w{1}\))?)\z)/
SESSION_ACTION = '_educoder_session'
validates :email, format: { with: VALID_EMAIL_REGEX }
validates :password, length: { minimum: 5, maximum: 50 }
validates_uniqueness_of :email, :if => Proc.new { |user| user.email_changed? && user.email.present? }, case_sensitive: false
@ -19,6 +19,15 @@ class User < ApplicationRecord
STATUS_LOCKED = 2
end
def self.current=(user)
RequestStore.store[:current_user] = user
end
def self.current
RequestStore.store[:current_user]
end
# 修改用户状态
def activate
self.status = STATUS_ACTIVE
@ -46,7 +55,7 @@ class User < ApplicationRecord
# Make sure no one can sign in with an empty login or password
return nil if login.empty?
if (login =~ VALID_EMAIL_REGEX)
user = find_by_mail(login)
user = find_by_email(login)
elsif (login =~ VALID_PHONE_REGEX)
user = find_by_phone(login)
else

@ -0,0 +1,2 @@
class UserAction < ApplicationRecord
end

@ -0,0 +1,52 @@
<%= form_with(model: token, local: true) do |form| %>
<% if token.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(token.errors.count, "error") %> prohibited this token from being saved:</h2>
<ul>
<% token.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :user_id %>
<%= form.number_field :user_id %>
</div>
<div class="field">
<%= form.label :action %>
<%= form.text_field :action %>
</div>
<div class="field">
<%= form.label :status %>
<%= form.number_field :status %>
</div>
<div class="field">
<%= form.label :value %>
<%= form.text_field :value %>
</div>
<div class="field">
<%= form.label :ip %>
<%= form.text_field :ip %>
</div>
<div class="field">
<%= form.label :browser %>
<%= form.text_field :browser %>
</div>
<div class="field">
<%= form.label :created_on %>
<%= form.date_select :created_on %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>

@ -0,0 +1,2 @@
json.extract! token, :id, :user_id, :action, :status, :value, :ip, :browser, :created_on, :created_at, :updated_at
json.url token_url(token, format: :json)

@ -0,0 +1,6 @@
<h1>Editing Token</h1>
<%= render 'form', token: @token %>
<%= link_to 'Show', @token %> |
<%= link_to 'Back', tokens_path %>

@ -0,0 +1,39 @@
<p id="notice"><%= notice %></p>
<h1>Tokens</h1>
<table>
<thead>
<tr>
<th>User</th>
<th>Action</th>
<th>Status</th>
<th>Value</th>
<th>Ip</th>
<th>Browser</th>
<th>Created on</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @tokens.each do |token| %>
<tr>
<td><%= token.user_id %></td>
<td><%= token.action %></td>
<td><%= token.status %></td>
<td><%= token.value %></td>
<td><%= token.ip %></td>
<td><%= token.browser %></td>
<td><%= token.created_on %></td>
<td><%= link_to 'Show', token %></td>
<td><%= link_to 'Edit', edit_token_path(token) %></td>
<td><%= link_to 'Destroy', token, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Token', new_token_path %>

@ -0,0 +1 @@
json.array! @tokens, partial: "tokens/token", as: :token

@ -0,0 +1,5 @@
<h1>New Token</h1>
<%= render 'form', token: @token %>
<%= link_to 'Back', tokens_path %>

@ -0,0 +1,39 @@
<p id="notice"><%= notice %></p>
<p>
<strong>User:</strong>
<%= @token.user_id %>
</p>
<p>
<strong>Action:</strong>
<%= @token.action %>
</p>
<p>
<strong>Status:</strong>
<%= @token.status %>
</p>
<p>
<strong>Value:</strong>
<%= @token.value %>
</p>
<p>
<strong>Ip:</strong>
<%= @token.ip %>
</p>
<p>
<strong>Browser:</strong>
<%= @token.browser %>
</p>
<p>
<strong>Created on:</strong>
<%= @token.created_on %>
</p>
<%= link_to 'Edit', edit_token_path(@token) %> |
<%= link_to 'Back', tokens_path %>

@ -0,0 +1 @@
json.partial! "tokens/token", token: @token

@ -0,0 +1,37 @@
<%= form_with(model: user_action, local: true) do |form| %>
<% if user_action.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_action.errors.count, "error") %> prohibited this user_action from being saved:</h2>
<ul>
<% user_action.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :user_id %>
<%= form.number_field :user_id %>
</div>
<div class="field">
<%= form.label :action_type %>
<%= form.text_field :action_type %>
</div>
<div class="field">
<%= form.label :action_id %>
<%= form.number_field :action_id %>
</div>
<div class="field">
<%= form.label :ip %>
<%= form.text_field :ip %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>

@ -0,0 +1,2 @@
json.extract! user_action, :id, :user_id, :action_type, :action_id, :ip, :created_at, :updated_at
json.url user_action_url(user_action, format: :json)

@ -0,0 +1,6 @@
<h1>Editing User Action</h1>
<%= render 'form', user_action: @user_action %>
<%= link_to 'Show', @user_action %> |
<%= link_to 'Back', user_actions_path %>

@ -0,0 +1,33 @@
<p id="notice"><%= notice %></p>
<h1>User Actions</h1>
<table>
<thead>
<tr>
<th>User</th>
<th>Action type</th>
<th>Action</th>
<th>Ip</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @user_actions.each do |user_action| %>
<tr>
<td><%= user_action.user_id %></td>
<td><%= user_action.action_type %></td>
<td><%= user_action.action_id %></td>
<td><%= user_action.ip %></td>
<td><%= link_to 'Show', user_action %></td>
<td><%= link_to 'Edit', edit_user_action_path(user_action) %></td>
<td><%= link_to 'Destroy', user_action, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New User Action', new_user_action_path %>

@ -0,0 +1 @@
json.array! @user_actions, partial: "user_actions/user_action", as: :user_action

@ -0,0 +1,5 @@
<h1>New User Action</h1>
<%= render 'form', user_action: @user_action %>
<%= link_to 'Back', user_actions_path %>

@ -0,0 +1,24 @@
<p id="notice"><%= notice %></p>
<p>
<strong>User:</strong>
<%= @user_action.user_id %>
</p>
<p>
<strong>Action type:</strong>
<%= @user_action.action_type %>
</p>
<p>
<strong>Action:</strong>
<%= @user_action.action_id %>
</p>
<p>
<strong>Ip:</strong>
<%= @user_action.ip %>
</p>
<%= link_to 'Edit', edit_user_action_path(@user_action) %> |
<%= link_to 'Back', user_actions_path %>

@ -0,0 +1 @@
json.partial! "user_actions/user_action", user_action: @user_action

@ -26,13 +26,14 @@ Rails.application.configure do
#
# config.cache_store = :null_store
# end
config.cache_store = :redis_cache_store, {
host: 'localhost', # Redis 服务器的主机名
port: 6379, # Redis 服务器的端口号
db: 0, # 使用的数据库编号
# password: 'yourpassword', # 如果有密码,填写密码
namespace: 'cache' # 缓存键的前缀
}
# config.cache_store = :redis_cache_store, {
# host: 'localhost', # Redis 服务器的主机名
# port: 6379, # Redis 服务器的端口号
# db: 0, # 使用的数据库编号
# # password: 'yourpassword', # 如果有密码,填写密码
# namespace: 'cache' # 缓存键的前缀
# }
config.cache_store = :redis_store#, "#{Rails.root}/files/cache_store/"
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local

@ -0,0 +1,11 @@
class CreateUserActions < ActiveRecord::Migration[5.2]
def change
create_table :user_actions do |t|
t.integer :user_id, comment: "用户id"
t.string :action_type, comment: "操作方式"
t.integer :action_id, comment: "操作id"
t.string :ip, comment: "ip地址"
t.timestamps
end
end
end

@ -0,0 +1,14 @@
class CreateTokens < ActiveRecord::Migration[5.2]
def change
create_table :tokens do |t|
t.integer :user_id, comment: "用户id"
t.string :action, comment: "方式"
t.integer :status, default: 0, comment: "status 0=正常登陆1=正常退出登陆失效2=考试期间输入考试密码登录3=考试期间被挤出登录失效4=同一个浏览器登录导致失效5考试期间进入考试导致其他登录用户失效"
t.string :value, comment: ""
t.string :ip
t.string :browser, comment: "登录的游览器"
t.date :created_on
end
end
end

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2023_08_31_073731) do
ActiveRecord::Schema.define(version: 2023_09_01_082819) do
create_table "edu_settings", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name"
@ -21,6 +21,25 @@ ActiveRecord::Schema.define(version: 2023_08_31_073731) do
t.index ["name"], name: "index_edu_settings_on_name", unique: true
end
create_table "tokens", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.integer "user_id", comment: "用户id"
t.string "action", comment: "方式"
t.integer "status", default: 0, comment: "status 0=正常登陆1=正常退出登陆失效2=考试期间输入考试密码登录3=考试期间被挤出登录失效4=同一个浏览器登录导致失效5考试期间进入考试导致其他登录用户失效"
t.string "value", comment: ""
t.string "ip"
t.string "browser", comment: "登录的游览器"
t.date "created_on"
end
create_table "user_actions", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.integer "user_id", comment: "用户id"
t.string "action_type", comment: "操作方式"
t.integer "action_id", comment: "操作id"
t.string "ip", comment: "ip地址"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "login", comment: "登录账号"
t.string "password", comment: "密码"

@ -0,0 +1,48 @@
require 'test_helper'
class TokensControllerTest < ActionDispatch::IntegrationTest
setup do
@token = tokens(:one)
end
test "should get index" do
get tokens_url
assert_response :success
end
test "should get new" do
get new_token_url
assert_response :success
end
test "should create token" do
assert_difference('Token.count') do
post tokens_url, params: { token: { action: @token.action, browser: @token.browser, created_on: @token.created_on, ip: @token.ip, status: @token.status, user_id: @token.user_id, value: @token.value } }
end
assert_redirected_to token_url(Token.last)
end
test "should show token" do
get token_url(@token)
assert_response :success
end
test "should get edit" do
get edit_token_url(@token)
assert_response :success
end
test "should update token" do
patch token_url(@token), params: { token: { action: @token.action, browser: @token.browser, created_on: @token.created_on, ip: @token.ip, status: @token.status, user_id: @token.user_id, value: @token.value } }
assert_redirected_to token_url(@token)
end
test "should destroy token" do
assert_difference('Token.count', -1) do
delete token_url(@token)
end
assert_redirected_to tokens_url
end
end

@ -0,0 +1,48 @@
require 'test_helper'
class UserActionsControllerTest < ActionDispatch::IntegrationTest
setup do
@user_action = user_actions(:one)
end
test "should get index" do
get user_actions_url
assert_response :success
end
test "should get new" do
get new_user_action_url
assert_response :success
end
test "should create user_action" do
assert_difference('UserAction.count') do
post user_actions_url, params: { user_action: { action_id: @user_action.action_id, action_type: @user_action.action_type, ip: @user_action.ip, user_id: @user_action.user_id } }
end
assert_redirected_to user_action_url(UserAction.last)
end
test "should show user_action" do
get user_action_url(@user_action)
assert_response :success
end
test "should get edit" do
get edit_user_action_url(@user_action)
assert_response :success
end
test "should update user_action" do
patch user_action_url(@user_action), params: { user_action: { action_id: @user_action.action_id, action_type: @user_action.action_type, ip: @user_action.ip, user_id: @user_action.user_id } }
assert_redirected_to user_action_url(@user_action)
end
test "should destroy user_action" do
assert_difference('UserAction.count', -1) do
delete user_action_url(@user_action)
end
assert_redirected_to user_actions_url
end
end

@ -0,0 +1,19 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
user_id: 1
action: MyString
status: 1
value: MyString
ip: MyString
browser: MyString
created_on: 2023-09-01
two:
user_id: 1
action: MyString
status: 1
value: MyString
ip: MyString
browser: MyString
created_on: 2023-09-01

@ -0,0 +1,13 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
user_id: 1
action_type: MyString
action_id: 1
ip: MyString
two:
user_id: 1
action_type: MyString
action_id: 1
ip: MyString

@ -0,0 +1,7 @@
require 'test_helper'
class TokenTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

@ -0,0 +1,7 @@
require 'test_helper'
class UserActionTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

@ -0,0 +1,55 @@
require "application_system_test_case"
class TokensTest < ApplicationSystemTestCase
setup do
@token = tokens(:one)
end
test "visiting the index" do
visit tokens_url
assert_selector "h1", text: "Tokens"
end
test "creating a Token" do
visit tokens_url
click_on "New Token"
fill_in "Action", with: @token.action
fill_in "Browser", with: @token.browser
fill_in "Created on", with: @token.created_on
fill_in "Ip", with: @token.ip
fill_in "Status", with: @token.status
fill_in "User", with: @token.user_id
fill_in "Value", with: @token.value
click_on "Create Token"
assert_text "Token was successfully created"
click_on "Back"
end
test "updating a Token" do
visit tokens_url
click_on "Edit", match: :first
fill_in "Action", with: @token.action
fill_in "Browser", with: @token.browser
fill_in "Created on", with: @token.created_on
fill_in "Ip", with: @token.ip
fill_in "Status", with: @token.status
fill_in "User", with: @token.user_id
fill_in "Value", with: @token.value
click_on "Update Token"
assert_text "Token was successfully updated"
click_on "Back"
end
test "destroying a Token" do
visit tokens_url
page.accept_confirm do
click_on "Destroy", match: :first
end
assert_text "Token was successfully destroyed"
end
end

@ -0,0 +1,49 @@
require "application_system_test_case"
class UserActionsTest < ApplicationSystemTestCase
setup do
@user_action = user_actions(:one)
end
test "visiting the index" do
visit user_actions_url
assert_selector "h1", text: "User Actions"
end
test "creating a User action" do
visit user_actions_url
click_on "New User Action"
fill_in "Action", with: @user_action.action_id
fill_in "Action type", with: @user_action.action_type
fill_in "Ip", with: @user_action.ip
fill_in "User", with: @user_action.user_id
click_on "Create User action"
assert_text "User action was successfully created"
click_on "Back"
end
test "updating a User action" do
visit user_actions_url
click_on "Edit", match: :first
fill_in "Action", with: @user_action.action_id
fill_in "Action type", with: @user_action.action_type
fill_in "Ip", with: @user_action.ip
fill_in "User", with: @user_action.user_id
click_on "Update User action"
assert_text "User action was successfully updated"
click_on "Back"
end
test "destroying a User action" do
visit user_actions_url
page.accept_confirm do
click_on "Destroy", match: :first
end
assert_text "User action was successfully destroyed"
end
end
Loading…
Cancel
Save