【私域操盘手】登录模块、设备管理部分接口
This commit is contained in:
9
.Cursorignore
Normal file
9
.Cursorignore
Normal file
@@ -0,0 +1,9 @@
|
||||
Server/runtime/
|
||||
*.png
|
||||
*.jpg
|
||||
*.jpeg
|
||||
*.gif
|
||||
*.bmp
|
||||
*.webp
|
||||
*.ico
|
||||
*.svg
|
||||
@@ -3,4 +3,6 @@ VUE_APP_PREVIEW=false
|
||||
VUE_APP_API_BASE_URL=http://yishi.com
|
||||
VUE_APP_WWW_BASE_URL=http://yishi.com
|
||||
VUE_APP_WEB_SOCKET_URL=ws://yishi.com:2348
|
||||
VUE_APP_WEBSITE_NAME="管理后台"
|
||||
VUE_APP_WEBSITE_NAME="管理后台"
|
||||
VUE_APP_TITLE=医师管理系统
|
||||
VUE_APP_API_URL=http://yishi.com
|
||||
@@ -1,6 +1,14 @@
|
||||
# 环境标识
|
||||
NODE_ENV=development
|
||||
VUE_APP_PREVIEW=true
|
||||
VUE_APP_PREVIEW=false
|
||||
# 应用名称
|
||||
VUE_APP_WEBSITE_NAME=艺施管理系统
|
||||
|
||||
# API基础URL
|
||||
VUE_APP_API_BASE_URL=http://yishi.com
|
||||
|
||||
# 前端网站
|
||||
VUE_APP_WWW_BASE_URL=http://yishi.com
|
||||
VUE_APP_WEB_SOCKET_URL=ws://yishi.com:2348
|
||||
VUE_APP_WEBSITE_NAME="管理后台"
|
||||
|
||||
# WebSocket URL
|
||||
VUE_APP_WEB_SOCKET_URL=wss://api.yishi.com/ws
|
||||
14
Backend/.env.production
Normal file
14
Backend/.env.production
Normal file
@@ -0,0 +1,14 @@
|
||||
# 环境标识
|
||||
NODE_ENV=production
|
||||
|
||||
# 应用名称
|
||||
VUE_APP_WEBSITE_NAME=医师管理系统
|
||||
|
||||
# API基础URL
|
||||
VUE_APP_API_BASE_URL=https://api.yishi.com
|
||||
|
||||
# 前端网站URL
|
||||
VUE_APP_WWW_BASE_URL=https://www.yishi.com
|
||||
|
||||
# WebSocket URL
|
||||
VUE_APP_WEB_SOCKET_URL=wss://api.yishi.com/ws
|
||||
14
Backend/.env.test
Normal file
14
Backend/.env.test
Normal file
@@ -0,0 +1,14 @@
|
||||
# 环境标识
|
||||
NODE_ENV=production
|
||||
|
||||
# 应用名称
|
||||
VUE_APP_WEBSITE_NAME=医师管理系统(测试)
|
||||
|
||||
# API基础URL
|
||||
VUE_APP_API_BASE_URL=https://test-api.yishi.com
|
||||
|
||||
# 前端网站URL
|
||||
VUE_APP_WWW_BASE_URL=https://test.yishi.com
|
||||
|
||||
# WebSocket URL
|
||||
VUE_APP_WEB_SOCKET_URL=wss://test-api.yishi.com/ws
|
||||
@@ -85,4 +85,82 @@ server {
|
||||
#### 联系方式
|
||||
QQ : 837215079
|
||||
|
||||
### 如果你觉得还不错,请 Star , Fork 给作者鼓励一下。
|
||||
### 如果你觉得还不错,请 Star , Fork 给作者鼓励一下。
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
本项目使用Vue CLI的环境变量配置功能,可以在不同环境下使用不同的配置。
|
||||
|
||||
### 配置文件
|
||||
|
||||
- `.env` - 所有环境的默认配置
|
||||
- `.env.development` - 开发环境配置
|
||||
- `.env.test` - 测试环境配置
|
||||
- `.env.production` - 生产环境配置
|
||||
|
||||
### 环境变量
|
||||
|
||||
项目中使用的主要环境变量:
|
||||
|
||||
- `VUE_APP_WEBSITE_NAME` - 网站名称
|
||||
- `VUE_APP_API_BASE_URL` - API基础URL
|
||||
- `VUE_APP_WWW_BASE_URL` - 前端网站URL
|
||||
- `VUE_APP_WEB_SOCKET_URL` - WebSocket URL
|
||||
|
||||
### 使用方法
|
||||
|
||||
在代码中可以通过以下方式访问环境变量:
|
||||
|
||||
```js
|
||||
// 直接访问
|
||||
console.log(process.env.VUE_APP_API_BASE_URL)
|
||||
|
||||
// 通过配置文件访问
|
||||
import config from '@/config/config'
|
||||
console.log(config.BASE_API_URL)
|
||||
```
|
||||
|
||||
### 运行与构建
|
||||
|
||||
开发环境:
|
||||
```bash
|
||||
# 使用开发环境配置运行
|
||||
npm run serve:dev
|
||||
|
||||
# 使用开发环境配置构建
|
||||
npm run build:dev
|
||||
```
|
||||
|
||||
测试环境:
|
||||
```bash
|
||||
# 使用测试环境配置运行
|
||||
npm run serve:test
|
||||
|
||||
# 使用测试环境配置构建
|
||||
npm run build:test
|
||||
```
|
||||
|
||||
生产环境:
|
||||
```bash
|
||||
# 使用生产环境配置运行
|
||||
npm run serve:prod
|
||||
|
||||
# 使用生产环境配置构建
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
## 项目设置
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 启动开发服务器
|
||||
npm run serve
|
||||
|
||||
# 构建生产版本
|
||||
npm run build
|
||||
|
||||
# 代码检查
|
||||
npm run lint
|
||||
```
|
||||
9464
Backend/package-lock.json
generated
9464
Backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,24 @@
|
||||
{
|
||||
"name": "YiShi",
|
||||
"version": "0.1.0",
|
||||
"name": "yishi-admin",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"serve:dev": "vue-cli-service serve --mode development",
|
||||
"serve:test": "vue-cli-service serve --mode test",
|
||||
"serve:prod": "vue-cli-service serve --mode production",
|
||||
"build": "vue-cli-service build",
|
||||
"build:dev": "vue-cli-service build --mode development",
|
||||
"build:test": "vue-cli-service build --mode test",
|
||||
"build:prod": "vue-cli-service build --mode production",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.0",
|
||||
"axios": "^0.21.1",
|
||||
"babel-plugin-prismjs": "^2.0.1",
|
||||
"core-js": "^3.6.5",
|
||||
"element-ui": "^2.14.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-ui": "^2.15.6",
|
||||
"js-audio-recorder": "^1.0.6",
|
||||
"js-base64": "^2.5.1",
|
||||
"mavon-editor": "^2.9.0",
|
||||
@@ -22,12 +29,14 @@
|
||||
"vue-contextmenujs": "^1.3.13",
|
||||
"vue-cropper": "^0.5.5",
|
||||
"vue-prism-editor": "^0.5.1",
|
||||
"vue-router": "^3.4.9",
|
||||
"vuex": "^3.5.1"
|
||||
"vue-router": "^3.2.0",
|
||||
"vuex": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-plugin-import": "^1.13.1",
|
||||
|
||||
@@ -1,12 +1,42 @@
|
||||
import { post } from '@/utils/request'
|
||||
import { post, get } from '@/utils/request'
|
||||
|
||||
// 登录服务接口
|
||||
export const ServeLogin = data => {
|
||||
return post('/backend/user/login', data)
|
||||
/**
|
||||
* 用户登录
|
||||
* @param {Object} data 登录数据
|
||||
* @param {string} data.username 用户名
|
||||
* @param {string} data.password 密码
|
||||
* @param {boolean} data.is_encrypted 密码是否已加密
|
||||
* @returns {Promise} 登录结果
|
||||
*/
|
||||
export function ServeLogin(data) {
|
||||
return post('/api/auth/login', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号验证码登录
|
||||
* @param {Object} data 登录数据
|
||||
* @param {string} data.mobile 手机号
|
||||
* @param {string} data.code 验证码
|
||||
* @param {boolean} data.is_encrypted 验证码是否已加密
|
||||
* @returns {Promise} 登录结果
|
||||
*/
|
||||
export function ServeMobileLogin(data) {
|
||||
return post('/api/auth/mobile-login', data)
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
export const ServeSendCode = data => {
|
||||
return post('/api/auth/code', data)
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
export const ServeGetUser = () => {
|
||||
return post('/backend/user/get')
|
||||
return get('/api/auth/info')
|
||||
}
|
||||
|
||||
// 刷新token
|
||||
export const ServeRefreshToken = () => {
|
||||
return post('/api/auth/refresh')
|
||||
}
|
||||
|
||||
export const ServeSetUserPassword = (data) => {
|
||||
@@ -14,8 +44,9 @@ export const ServeSetUserPassword = (data) => {
|
||||
}
|
||||
|
||||
// 退出登录服务接口
|
||||
export const ServeLogout = data => {
|
||||
return post('/backend/user/logout', data)
|
||||
export const ServeLogout = () => {
|
||||
// JWT不需要服务端登出,直接清除本地token即可
|
||||
return Promise.resolve({ code: 200, msg: '退出成功' })
|
||||
}
|
||||
|
||||
export const UserIndex = data => {
|
||||
|
||||
@@ -7,10 +7,18 @@ export default {
|
||||
{
|
||||
path: '/auth/login',
|
||||
meta: {
|
||||
title: '账号登录?',
|
||||
title: '账号登录',
|
||||
needLogin: false,
|
||||
},
|
||||
component: () => import('@/views/auth/login'),
|
||||
},
|
||||
{
|
||||
path: '/auth/mobile-login',
|
||||
meta: {
|
||||
title: '手机号登录',
|
||||
needLogin: false,
|
||||
},
|
||||
component: () => import('@/views/auth/mobile-login'),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import SystemRouter from './system'
|
||||
import ProductRouter from '@/router/product';
|
||||
import TaskRouter from '@/router/task';
|
||||
import DeviceRouter from '@/router/device';
|
||||
import { isLogin } from '@/utils/auth'
|
||||
import store from '@/store'
|
||||
|
||||
const originalPush = Router.prototype.push
|
||||
Router.prototype.push = function push(location) {
|
||||
@@ -46,7 +48,33 @@ const routes = [
|
||||
},
|
||||
]
|
||||
|
||||
export default new Router({
|
||||
const router = new Router({
|
||||
routes,
|
||||
mode: 'hash',
|
||||
})
|
||||
|
||||
// 全局前置守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
// 设置页面标题
|
||||
document.title = to.meta.title ? to.meta.title : '医视管理系统'
|
||||
|
||||
// 检查路由是否需要登录
|
||||
if (to.meta.needLogin) {
|
||||
// 检查登录状态
|
||||
if (isLogin()) {
|
||||
next()
|
||||
} else {
|
||||
// 未登录,重定向到登录页
|
||||
next({ path: '/auth/login' })
|
||||
}
|
||||
} else {
|
||||
// 如果是访问登录页且已登录,则跳转到首页
|
||||
if (to.path === '/auth/login' && isLogin()) {
|
||||
next({ path: '/' })
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
import { setUserInfo, getUserInfo, removeAll, getToken } from '@/utils/auth'
|
||||
import { setUserInfo, getUserInfo, removeAll, getToken, isLogin } from '@/utils/auth'
|
||||
|
||||
import { ServeLogout } from '@/api/user'
|
||||
import { ServeLogout, ServeRefreshToken } from '@/api/user'
|
||||
|
||||
let state = {
|
||||
// 用户ID
|
||||
id: 0,
|
||||
// 渠道ID
|
||||
channel_id: 0,
|
||||
// 渠道名称
|
||||
channel_name: '',
|
||||
// 角色
|
||||
role: '',
|
||||
// 权限
|
||||
permissions: [],
|
||||
// 账号
|
||||
username: '',
|
||||
// 姓名
|
||||
name: '',
|
||||
// 手机号
|
||||
mobile: '',
|
||||
// 登录时间
|
||||
login_time: '',
|
||||
// 登录次数
|
||||
login_count: 0,
|
||||
// 登录IP
|
||||
login_ip: '',
|
||||
// 创建时间
|
||||
create_time: '',
|
||||
// 个性头像
|
||||
avatar: require('@/assets/image/detault-avatar.jpg'),
|
||||
// 角色
|
||||
roles: [],
|
||||
// 当前登录状态
|
||||
loginStatus: false,
|
||||
// 是否启动游戏模块
|
||||
// 原有字段保持不变
|
||||
channel_id: 0,
|
||||
channel_name: '',
|
||||
mobile: '',
|
||||
login_time: '',
|
||||
login_count: 0,
|
||||
login_ip: '',
|
||||
create_time: '',
|
||||
mod_game: false,
|
||||
}
|
||||
|
||||
// 判断用户是否登录
|
||||
if (getToken()) {
|
||||
if (isLogin()) {
|
||||
let userInfo = getUserInfo()
|
||||
|
||||
state.id = userInfo.id
|
||||
state.channel_id = userInfo.channel_id
|
||||
state.channel_name = userInfo.channel_name
|
||||
state.username = userInfo.username
|
||||
state.name = userInfo.name
|
||||
state.mobile = userInfo.mobile
|
||||
state.login_time = userInfo.login_time
|
||||
state.login_count = userInfo.login_count
|
||||
state.login_ip = userInfo.login_ip
|
||||
state.create_time = userInfo.create_time
|
||||
state.roles = userInfo.roles ? userInfo.roles: []
|
||||
//state.avatar = userInfo.avatar ? userInfo.avatar : state.avatar
|
||||
// 更新状态
|
||||
state.id = userInfo.id
|
||||
state.username = userInfo.username
|
||||
state.name = userInfo.name
|
||||
state.role = userInfo.role
|
||||
state.permissions = userInfo.permissions || []
|
||||
state.loginStatus = true
|
||||
state.mod_game = userInfo.mod_game
|
||||
|
||||
// 兼容原有字段
|
||||
state.channel_id = userInfo.channel_id || 0
|
||||
state.channel_name = userInfo.channel_name || ''
|
||||
state.mobile = userInfo.mobile || ''
|
||||
state.login_time = userInfo.login_time || ''
|
||||
state.login_count = userInfo.login_count || 0
|
||||
state.login_ip = userInfo.login_ip || ''
|
||||
state.create_time = userInfo.create_time || ''
|
||||
state.mod_game = userInfo.mod_game || false
|
||||
state.roles = userInfo.roles || []
|
||||
|
||||
console.log('userInfo: ', userInfo)
|
||||
}
|
||||
@@ -60,24 +59,28 @@ const User = {
|
||||
mutations: {
|
||||
// 用户退出登录
|
||||
USER_LOGOUT(state) {
|
||||
state.id = 0
|
||||
state.channel_id = 0
|
||||
state.channel_name = ''
|
||||
state.username = ''
|
||||
state.name = ''
|
||||
state.mobile = ''
|
||||
state.login_time = ''
|
||||
state.login_count = 0
|
||||
state.login_ip = ''
|
||||
state.create_time = ''
|
||||
state.roles = []
|
||||
state.id = 0
|
||||
state.username = ''
|
||||
state.name = ''
|
||||
state.role = ''
|
||||
state.permissions = []
|
||||
state.loginStatus = false
|
||||
state.mod_game = false
|
||||
|
||||
// 原有字段重置
|
||||
state.channel_id = 0
|
||||
state.channel_name = ''
|
||||
state.mobile = ''
|
||||
state.login_time = ''
|
||||
state.login_count = 0
|
||||
state.login_ip = ''
|
||||
state.create_time = ''
|
||||
state.roles = []
|
||||
state.mod_game = false
|
||||
},
|
||||
|
||||
// 设置用户登录状态
|
||||
UPDATE_LOGIN_STATUS(state) {
|
||||
state.loginStatus = true
|
||||
UPDATE_LOGIN_STATUS(state, status) {
|
||||
state.loginStatus = status
|
||||
},
|
||||
|
||||
// 更新用户信息
|
||||
@@ -91,18 +94,20 @@ const User = {
|
||||
// 保存用户信息到缓存
|
||||
setUserInfo({
|
||||
id: state.id,
|
||||
channel_id: state.channel_id,
|
||||
channel_name: state.channel_name,
|
||||
username: state.username,
|
||||
name: state.name,
|
||||
role: state.role,
|
||||
permissions: state.permissions || [],
|
||||
// 兼容原有字段
|
||||
channel_id: state.channel_id,
|
||||
channel_name: state.channel_name,
|
||||
mobile: state.mobile,
|
||||
login_time: state.login_time,
|
||||
login_count: state.login_count,
|
||||
login_ip: state.login_ip,
|
||||
create_time: state.create_time,
|
||||
roles: state.roles ? state.roles: [],
|
||||
roles: state.roles || [],
|
||||
mod_game: state.mod_game,
|
||||
//avatar: state.avatar,
|
||||
})
|
||||
},
|
||||
},
|
||||
@@ -115,6 +120,25 @@ const User = {
|
||||
location.reload()
|
||||
})
|
||||
},
|
||||
|
||||
// 刷新令牌
|
||||
ACT_REFRESH_TOKEN({ commit }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ServeRefreshToken()
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
// 更新token
|
||||
setToken(res.data.token, res.data.token_expired - Math.floor(Date.now() / 1000))
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ const USER_SETTING = 'MANAGE_SETTING'
|
||||
/**
|
||||
* 设置用户授权token
|
||||
*
|
||||
* @param {String} token
|
||||
* @param {Number} expires
|
||||
* @param {String} token - JWT令牌
|
||||
* @param {Number} expires - 过期时间戳(秒)
|
||||
*/
|
||||
export function setToken(token, expires) {
|
||||
expires = new Date().getTime() + expires * 1000
|
||||
@@ -23,6 +23,7 @@ export function setToken(token, expires) {
|
||||
|
||||
/**
|
||||
* 获取授权token
|
||||
* @returns {String} token
|
||||
*/
|
||||
export function getToken() {
|
||||
const result = JSON.parse(
|
||||
@@ -33,9 +34,24 @@ export function getToken() {
|
||||
})
|
||||
)
|
||||
|
||||
// 检查token是否过期
|
||||
if (result.expires > 0 && result.expires < new Date().getTime()) {
|
||||
// token已过期,清除token
|
||||
removeAll()
|
||||
return ''
|
||||
}
|
||||
|
||||
return result.token
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否登录
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isLogin() {
|
||||
return !!getToken()
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户信息
|
||||
*
|
||||
|
||||
36
Backend/src/utils/crypto.js
Normal file
36
Backend/src/utils/crypto.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
/**
|
||||
* 密码加密工具类
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* MD5加密
|
||||
* @param {string} text 需要加密的文本
|
||||
* @returns {string} 加密后的文本
|
||||
*/
|
||||
md5(text) {
|
||||
return CryptoJS.MD5(text).toString()
|
||||
},
|
||||
|
||||
/**
|
||||
* SHA256加密
|
||||
* @param {string} text 需要加密的文本
|
||||
* @returns {string} 加密后的文本
|
||||
*/
|
||||
sha256(text) {
|
||||
return CryptoJS.SHA256(text).toString()
|
||||
},
|
||||
|
||||
/**
|
||||
* 密码加密
|
||||
* 使用SHA256加密,可以根据需要修改为其他算法
|
||||
* @param {string} password 原始密码
|
||||
* @returns {string} 加密后的密码
|
||||
*/
|
||||
encryptPassword(password) {
|
||||
// 可以添加自定义的盐值增加安全性
|
||||
const salt = 'yishi_salt_2024'
|
||||
return this.sha256(password + salt)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,12 @@ const request = axios.create({
|
||||
timeout: 20000,
|
||||
})
|
||||
|
||||
// 输出当前环境和API基础URL(仅在开发环境)
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('当前环境:', process.env.NODE_ENV)
|
||||
console.log('API基础URL:', config.BASE_API_URL)
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常拦截处理器
|
||||
*
|
||||
@@ -39,7 +45,8 @@ const errorHandler = error => {
|
||||
request.interceptors.request.use(config => {
|
||||
const token = getToken()
|
||||
if (token) {
|
||||
config.headers['token'] = token
|
||||
// 设置JWT认证头
|
||||
config.headers['Authorization'] = 'Bearer ' + token
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
>立即登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<div class="login-options">
|
||||
<span @click="toMobileLogin">手机号登录</span>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { setToken } from '@/utils/auth'
|
||||
import { setToken, setUserInfo } from '@/utils/auth'
|
||||
import { ServeLogin } from '@/api/user'
|
||||
import CryptoUtil from '@/utils/crypto'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -76,25 +80,37 @@ export default {
|
||||
},
|
||||
|
||||
login() {
|
||||
// 对密码进行加密
|
||||
const encryptedPassword = CryptoUtil.encryptPassword(this.form.password)
|
||||
|
||||
ServeLogin({
|
||||
username: this.form.username,
|
||||
password: this.form.password,
|
||||
password: encryptedPassword,
|
||||
is_encrypted: true // 标记密码已加密
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
let result = res.data
|
||||
|
||||
// 保存授权信息到本地缓存
|
||||
setToken(result.token, result.token_expired)
|
||||
setToken(result.token, result.token_expired - Math.floor(Date.now() / 1000))
|
||||
|
||||
// 保存用户信息到本地缓存
|
||||
setUserInfo(result.member)
|
||||
|
||||
this.$store.commit('UPDATE_USER_INFO', result.member)
|
||||
this.$store.commit('UPDATE_LOGIN_STATUS')
|
||||
this.$store.commit('UPDATE_LOGIN_STATUS', true)
|
||||
|
||||
// 登录成功后连接 WebSocket 服务器
|
||||
this.$notify.success({
|
||||
title: '成功',
|
||||
message: '登录成功',
|
||||
})
|
||||
|
||||
// 跳转到首页
|
||||
this.toLink('/')
|
||||
} else {
|
||||
this.$notify.info({
|
||||
title: '提示',
|
||||
this.$notify.error({
|
||||
title: '错误',
|
||||
message: res.msg,
|
||||
})
|
||||
}
|
||||
@@ -104,6 +120,10 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
toMobileLogin() {
|
||||
this.$router.push('/auth/mobile-login')
|
||||
},
|
||||
|
||||
toLink(url) {
|
||||
this.$router.push({
|
||||
path: url,
|
||||
@@ -114,4 +134,12 @@ export default {
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '~@/assets/css/page/login-auth.less';
|
||||
|
||||
.login-options {
|
||||
text-align: right;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #409EFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
239
Backend/src/views/auth/mobile-login.vue
Normal file
239
Backend/src/views/auth/mobile-login.vue
Normal file
@@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<div id="login-box">
|
||||
<div class="header">手机号登录</div>
|
||||
<div class="main">
|
||||
<el-form ref="form" :model="form" :rules="rules">
|
||||
<el-form-item prop="mobile">
|
||||
<el-input
|
||||
v-model="form.mobile"
|
||||
placeholder="手机号"
|
||||
class="cuborder-radius"
|
||||
maxlength="11"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<div class="code-input">
|
||||
<el-input
|
||||
v-model="form.code"
|
||||
placeholder="验证码"
|
||||
class="cuborder-radius"
|
||||
maxlength="6"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="code-btn"
|
||||
:disabled="codeBtnDisabled"
|
||||
@click="sendCode"
|
||||
>{{ codeBtnText }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="submit-btn"
|
||||
:loading="loginLoading"
|
||||
@click="onSubmit('form')"
|
||||
>立即登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<div class="login-options">
|
||||
<span @click="toAccountLogin">账号密码登录</span>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { setToken, setUserInfo } from '@/utils/auth'
|
||||
import { ServeMobileLogin, ServeSendCode } from '@/api/user'
|
||||
import CryptoUtil from '@/utils/crypto'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
// 手机号验证规则
|
||||
const validateMobile = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
return callback(new Error('请输入手机号'))
|
||||
}
|
||||
if (!/^1[3-9]\d{9}$/.test(value)) {
|
||||
return callback(new Error('手机号格式不正确'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
return {
|
||||
loginLoading: false,
|
||||
form: {
|
||||
mobile: '',
|
||||
code: '',
|
||||
},
|
||||
rules: {
|
||||
mobile: [
|
||||
{ required: true, message: '手机号不能为空', trigger: 'blur' },
|
||||
{ validator: validateMobile, trigger: 'blur' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '验证码不能为空', trigger: 'blur' },
|
||||
{ min: 4, max: 6, message: '验证码长度必须在4-6位之间', trigger: 'blur' }
|
||||
],
|
||||
},
|
||||
codeBtnText: '获取验证码',
|
||||
codeBtnDisabled: false,
|
||||
countdown: 60,
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁前清除定时器
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit(formName) {
|
||||
if (this.loginLoading) return false
|
||||
|
||||
this.$refs[formName].validate(valid => {
|
||||
if (!valid) return false
|
||||
this.loginLoading = true
|
||||
this.login()
|
||||
})
|
||||
},
|
||||
|
||||
login() {
|
||||
// 不再对验证码进行加密,直接使用明文
|
||||
ServeMobileLogin({
|
||||
mobile: this.form.mobile,
|
||||
code: this.form.code,
|
||||
is_encrypted: false // 标记验证码未加密
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
let result = res.data
|
||||
|
||||
// 保存授权信息到本地缓存
|
||||
setToken(result.token, result.token_expired - Math.floor(Date.now() / 1000))
|
||||
|
||||
// 保存用户信息到本地缓存
|
||||
setUserInfo(result.member)
|
||||
|
||||
this.$store.commit('UPDATE_USER_INFO', result.member)
|
||||
this.$store.commit('UPDATE_LOGIN_STATUS', true)
|
||||
|
||||
this.$notify.success({
|
||||
title: '成功',
|
||||
message: '登录成功',
|
||||
})
|
||||
|
||||
// 跳转到首页
|
||||
this.toLink('/')
|
||||
} else {
|
||||
this.$notify.error({
|
||||
title: '错误',
|
||||
message: res.msg,
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loginLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 发送验证码
|
||||
sendCode() {
|
||||
if (this.codeBtnDisabled) return
|
||||
|
||||
// 验证手机号
|
||||
this.$refs.form.validateField('mobile', (errorMsg) => {
|
||||
if (errorMsg) return
|
||||
|
||||
this.codeBtnDisabled = true
|
||||
|
||||
// 发送验证码请求
|
||||
ServeSendCode({
|
||||
mobile: this.form.mobile,
|
||||
type: 'login'
|
||||
}).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$notify.success({
|
||||
title: '成功',
|
||||
message: '验证码发送成功',
|
||||
})
|
||||
|
||||
// 开始倒计时
|
||||
this.startCountdown()
|
||||
|
||||
// 测试环境下,自动填充验证码
|
||||
if (res.data && res.data.code) {
|
||||
this.form.code = res.data.code
|
||||
}
|
||||
} else {
|
||||
this.$notify.error({
|
||||
title: '错误',
|
||||
message: res.msg,
|
||||
})
|
||||
this.codeBtnDisabled = false
|
||||
}
|
||||
}).catch(() => {
|
||||
this.codeBtnDisabled = false
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 开始倒计时
|
||||
startCountdown() {
|
||||
this.countdown = 60
|
||||
this.codeBtnText = `${this.countdown}秒后重新获取`
|
||||
|
||||
this.timer = setInterval(() => {
|
||||
if (this.countdown > 1) {
|
||||
this.countdown--
|
||||
this.codeBtnText = `${this.countdown}秒后重新获取`
|
||||
} else {
|
||||
clearInterval(this.timer)
|
||||
this.codeBtnDisabled = false
|
||||
this.codeBtnText = '获取验证码'
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
// 跳转到账号密码登录
|
||||
toAccountLogin() {
|
||||
this.$router.push('/auth/login')
|
||||
},
|
||||
|
||||
toLink(url) {
|
||||
this.$router.push({
|
||||
path: url,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '~@/assets/css/page/login-auth.less';
|
||||
|
||||
.code-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.el-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.code-btn {
|
||||
margin-left: 10px;
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-options {
|
||||
text-align: right;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #409EFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
247
Backend/src/views/device/detail.vue
Normal file
247
Backend/src/views/device/detail.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<el-container v-loading="loading" style="height: 100%;">
|
||||
<el-header style="display: flex; align-items: center; padding: 0;">
|
||||
<div style="margin-left: 20px;">
|
||||
<el-page-header @back="goBack" :content="'设备ID: ' + deviceId"></el-page-header>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-card v-if="deviceInfo" class="device-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span>设备详情</span>
|
||||
<el-tag :type="getStatusType(deviceInfo.status)" class="status-tag">
|
||||
{{ getStatusText(deviceInfo.status) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">设备ID:</span>
|
||||
<span class="value">{{ deviceInfo.id }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">IMEI:</span>
|
||||
<span class="value">{{ deviceInfo.imei }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">设备手机号:</span>
|
||||
<span class="value">{{ deviceInfo.phone || '--' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">电量:</span>
|
||||
<span class="value">
|
||||
<el-progress
|
||||
:percentage="deviceInfo.battery"
|
||||
:color="getBatteryColor(deviceInfo.battery)"
|
||||
:format="() => `${deviceInfo.battery}%`"
|
||||
style="width: 200px;">
|
||||
</el-progress>
|
||||
</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">微信ID:</span>
|
||||
<span class="value">{{ deviceInfo.wechat_id || '--' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">微信号:</span>
|
||||
<span class="value">{{ deviceInfo.wechat_name || '--' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">分组名称:</span>
|
||||
<span class="value">{{ deviceInfo.group_name || '--' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">保管者:</span>
|
||||
<span class="value">{{ deviceInfo.username || '--' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<div class="info-item">
|
||||
<span class="label">备注:</span>
|
||||
<span class="value">{{ deviceInfo.remark || '--' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">创建时间:</span>
|
||||
<span class="value">{{ formatTime(deviceInfo.create_time) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<span class="label">更新时间:</span>
|
||||
<span class="value">{{ formatTime(deviceInfo.update_time) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-empty v-else description="未找到设备信息"></el-empty>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 引入API请求
|
||||
import request from '@/utils/request'
|
||||
|
||||
export default {
|
||||
name: 'DeviceDetail',
|
||||
data() {
|
||||
return {
|
||||
// 设备ID
|
||||
deviceId: null,
|
||||
// 设备信息
|
||||
deviceInfo: null,
|
||||
// 加载状态
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 获取路由参数中的设备ID
|
||||
this.deviceId = this.$route.params.id
|
||||
// 加载设备详情
|
||||
this.getDeviceDetail()
|
||||
},
|
||||
methods: {
|
||||
// 返回列表页
|
||||
goBack() {
|
||||
this.$router.push('/device/index')
|
||||
},
|
||||
// 获取设备详情
|
||||
getDeviceDetail() {
|
||||
if (!this.deviceId) {
|
||||
this.$message.error('设备ID不能为空')
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
request({
|
||||
url: `/api/devices/detail/${this.deviceId}`,
|
||||
method: 'get'
|
||||
}).then(res => {
|
||||
this.loading = false
|
||||
if (res.code === 200) {
|
||||
this.deviceInfo = res.data
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取设备详情失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 格式化时间
|
||||
formatTime(timestamp) {
|
||||
if (!timestamp) return '--'
|
||||
const date = new Date(timestamp * 1000)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hour = date.getHours().toString().padStart(2, '0')
|
||||
const minute = date.getMinutes().toString().padStart(2, '0')
|
||||
const second = date.getSeconds().toString().padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||
},
|
||||
// 获取状态对应的类型
|
||||
getStatusType(status) {
|
||||
const statusMap = {
|
||||
'0': 'info', // 离线
|
||||
'1': 'success', // 在线
|
||||
'2': 'danger', // 故障
|
||||
'3': 'warning' // 维修中
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
},
|
||||
// 获取状态对应的文本
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
'0': '离线',
|
||||
'1': '在线',
|
||||
'2': '故障',
|
||||
'3': '维修中'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
},
|
||||
// 获取电量对应的颜色
|
||||
getBatteryColor(battery) {
|
||||
if (battery < 20) {
|
||||
return '#F56C6C' // 红色,电量低
|
||||
} else if (battery < 50) {
|
||||
return '#E6A23C' // 黄色,电量中等
|
||||
} else {
|
||||
return '#67C23A' // 绿色,电量充足
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-header {
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.device-card {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #606266;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
min-width: 80px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #303133;
|
||||
}
|
||||
</style>
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\CollectProductModel;
|
||||
use app\common\model\ProductGroupModel;
|
||||
use think\db\Query;
|
||||
|
||||
class CollectProductController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$parentId = intval($this->request->param('parent_id'));
|
||||
$type = trim($this->request->param('type'));
|
||||
$video = trim($this->request->param('video'));
|
||||
$repeat = trim($this->request->param('repeat'));
|
||||
$groupId = intval($this->request->param('group_id'));
|
||||
$status = trim($this->request->param('status'));
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = CollectProductModel::where(1);
|
||||
if (!empty($parentId)) {
|
||||
$query->where('parent_id', $parentId);
|
||||
} else {
|
||||
$query->where('parent_id', 0);
|
||||
}
|
||||
if (isset(CollectProductModel::typeAssoc()[$type])) {
|
||||
$query->where('type', $type);
|
||||
}
|
||||
if (isset(CollectProductModel::videoAssoc()[$video])) {
|
||||
$query->where('video', $video);
|
||||
}
|
||||
if (isset(CollectProductModel::repeatAssoc()[$repeat])) {
|
||||
$query->where('repeat', $repeat);
|
||||
}
|
||||
if (isset(CollectProductModel::statusAssoc()[$status])) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
if (!empty($groupId)) {
|
||||
$query->where('group_id', $groupId);
|
||||
}
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('src_url', '%' . $keywords . '%', 'OR');
|
||||
$q->whereLike('title', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'product_num' => $model->productNum(),
|
||||
'status_name' => CollectProductModel::statusAssoc()[$model->status],
|
||||
'video_name' => CollectProductModel::videoAssoc()[$model->video],
|
||||
'repeat_name' => CollectProductModel::repeatAssoc()[$model->repeat],
|
||||
'platform_name' => CollectProductModel::platformAssoc()[$model->platform],
|
||||
'mark_up_rate' => floatval($model->mark_up_rate),
|
||||
'mark_up_val' => floatval($model->mark_up_val),
|
||||
'start_time' => $model->start_time > 0 ? date('Y-m-d H:i:s', $model->start_time) : '',
|
||||
'stop_time' => $model->stop_time > 0 ? date('Y-m-d H:i:s', $model->stop_time) : '',
|
||||
'group_name' => isset(ProductGroupModel::assoc()[$model->group_id])
|
||||
? ProductGroupModel::assoc()[$model->group_id]
|
||||
: '',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
'statuses' => $this->assocToList(CollectProductModel::statusAssoc()),
|
||||
'platforms' => $this->assocToList(CollectProductModel::platformAssoc()),
|
||||
'videos' => $this->assocToList(CollectProductModel::videoAssoc()),
|
||||
'repeats' => $this->assocToList(CollectProductModel::repeatAssoc()),
|
||||
'groups' => $this->assocToList(ProductGroupModel::assoc()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save() {
|
||||
//$id = intval($this->request->param('id'));
|
||||
$type = trim($this->request->param('type'));
|
||||
$srcUrl = trim($this->request->param('src_url'));
|
||||
$video = intval($this->request->param('video'));
|
||||
$repeat = intval($this->request->param('repeat'));
|
||||
$markUpRate = intval($this->request->param('mark_up_rate'));
|
||||
$markUpVal = intval($this->request->param('mark_up_val'));
|
||||
$groupId = intval($this->request->param('group_id'));
|
||||
if (empty($srcUrl)
|
||||
OR !isset(CollectProductModel::typeAssoc()[$type])
|
||||
OR !isset(CollectProductModel::videoAssoc()[$video])
|
||||
OR !isset(CollectProductModel::repeatAssoc()[$repeat])) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
if (!empty($id)) {
|
||||
$model = CollectProductModel::get($id);
|
||||
if (empty($model)) {
|
||||
return $this->jsonFail('对象未找到');
|
||||
}
|
||||
} else {
|
||||
$model = new CollectProductModel();
|
||||
}
|
||||
|
||||
$platform = '';
|
||||
if ($type === CollectProductModel::TYPE_PRODUCT
|
||||
AND preg_match('#^https\:\/\/www\.goofish\.com\/item#', $srcUrl)) {
|
||||
$platform = CollectProductModel::PLATFORM_XIANYU;
|
||||
} elseif ($type === CollectProductModel::TYPE_SHOP
|
||||
AND preg_match('#^https\:\/\/www\.goofish\.com\/personal#', $srcUrl)) {
|
||||
$platform = CollectProductModel::PLATFORM_XIANYU;
|
||||
} else {
|
||||
return $this->jsonFail('采集链接错误');
|
||||
}
|
||||
|
||||
$model = new CollectProductModel();
|
||||
$model->type = $type;
|
||||
$model->src_url = $srcUrl;
|
||||
$model->platform = $platform;
|
||||
$model->target = CollectProductModel::TARGET_PRODUCT;
|
||||
$model->video = $video;
|
||||
$model->repeat = $repeat;
|
||||
$model->mark_up_rate = $markUpRate;
|
||||
$model->mark_up_val = $markUpVal;
|
||||
$model->group_id = $groupId;
|
||||
$model->user_id = $this->userModel->id;
|
||||
$model->save();
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\DeviceModel;
|
||||
use think\db\Query;
|
||||
|
||||
class DeviceController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$isOnline = intval($this->request->param('is_online'));
|
||||
$status = trim($this->request->param('status'));
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$onlines = [
|
||||
1 => '在线',
|
||||
2 => '离线',
|
||||
];
|
||||
|
||||
$query = DeviceModel::where(1);
|
||||
if (isset(DeviceModel::statusAssoc()[$status])) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
if ($isOnline == 1) {
|
||||
$query->where('is_online', DeviceModel::IS_ONLINE_YES);
|
||||
$query->where('active_time', '>=', time() - DeviceModel::ACTIVE_TIME);
|
||||
} elseif ($isOnline == 2) {
|
||||
$query->where(function (Query $q) {
|
||||
$q->whereOr('is_online', DeviceModel::IS_ONLINE_NO);
|
||||
$q->whereOr('active_time', '<', time() - DeviceModel::ACTIVE_TIME);
|
||||
});
|
||||
}
|
||||
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('number', '%' . $keywords . '%', 'OR');
|
||||
$q->whereLike('ip', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'is_online' => $model->isOnline() ? 1 : 2,
|
||||
'is_online_name' => $onlines[$model->isOnline() ? 1 : 2],
|
||||
'status_name' => DeviceModel::statusAssoc()[$model->status],
|
||||
'active_time' => date('Y-m-d H:i:s', $model->active_time),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
'statuses' => $this->assocToList(DeviceModel::statusAssoc()),
|
||||
'onlines' => $this->assocToList($onlines),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联数组
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function assoc() {
|
||||
return $this->jsonSucc($this->assocToList(DeviceModel::assoc()));
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use think\db\Query;
|
||||
|
||||
class DeviceStatController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = DeviceStatModel::where(1);
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('days', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
if ($pageNo <= 1) {
|
||||
$num = DeviceQqModel::where(1)
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->count();
|
||||
$succNum = DeviceQqModel::where(1)
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->where('status', DeviceQqModel::STATUS_SUCC)
|
||||
->count();
|
||||
$rewardTotal = floatval(DeviceQqModel::where(1)
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->where('status', DeviceQqModel::STATUS_SUCC)
|
||||
->sum('reward'));
|
||||
$rewardPrice = 0;
|
||||
if ($succNum > 0) {
|
||||
$rewardPrice = round($rewardTotal / $succNum, 2);
|
||||
}
|
||||
$finishTime = intval(DeviceQqModel::where(1)
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->where('finish_time', '>', 0)
|
||||
->avg('finish_time'));
|
||||
$createTime = intval(DeviceQqModel::where(1)
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->where('finish_time', '>', 0)
|
||||
->avg('create_time'));
|
||||
$succFinishTime = intval(DeviceQqModel::where(1)
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->where('status', DeviceQqModel::STATUS_SUCC)
|
||||
->where('finish_time', '>', 0)
|
||||
->avg('finish_time'));
|
||||
$succCreateTime = intval(DeviceQqModel::where(1)
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->where('status', DeviceQqModel::STATUS_SUCC)
|
||||
->where('finish_time', '>', 0)
|
||||
->avg('create_time'));
|
||||
|
||||
$succRate = $num > 0 ? round($succNum / $num * 100, 2) : 0;
|
||||
$failNum = $num - $succNum;
|
||||
$failRate = $num > 0 ? round($failNum / $num * 100, 2) : 0;
|
||||
$succFailRate = 0;
|
||||
if ($num > $succNum) {
|
||||
$succFailRate = $succNum / ($num - $succNum);
|
||||
$succFailRate = round($succFailRate * 100, 2);
|
||||
}
|
||||
|
||||
$failInfo = [];
|
||||
foreach (DeviceQqModel::where(1)
|
||||
->field('remark, COUNT(id) AS num')
|
||||
->where('create_time', '>=', 1724860800)
|
||||
->whereNotIn('status', [DeviceQqModel::STATUS_REG, DeviceQqModel::STATUS_SUCC])
|
||||
->group('remark')
|
||||
->select() as $infoModel) {
|
||||
if ($infoModel->remark) {
|
||||
|
||||
$failInfo[] = [
|
||||
'title' => $infoModel->remark,
|
||||
'num' => $infoModel->num,
|
||||
'rate' => $num > 0 ? round($infoModel->num / $num * 100, 2) : 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$list[] = [
|
||||
'days' => '合计',
|
||||
'num' => $num,
|
||||
'succ_num' => $succNum,
|
||||
'reward_price' => $rewardPrice,
|
||||
'reward_total' => $rewardTotal,
|
||||
'avg_time' => $finishTime > $createTime ? $finishTime - $createTime : 0,
|
||||
'avg_succ_time' => $succFinishTime > $succCreateTime ? $succFinishTime - $succCreateTime : 0,
|
||||
'succ_fail_rate' => $succFailRate,
|
||||
'succ_rate' => $succRate,
|
||||
'fail_num' => $failNum,
|
||||
'fail_rate' => $failRate,
|
||||
'fail_info' => $failInfo,
|
||||
];
|
||||
}
|
||||
foreach ($query->select() as $model) {
|
||||
$succRate = 0;
|
||||
$failNum = $model->num > $model->succ_num ? $model->num - $model->succ_num : 0;
|
||||
$failRate = 0;
|
||||
$succFailRate = 0;
|
||||
if ($model->num > 0) {
|
||||
$succRate = $model->succ_num / $model->num;
|
||||
$succRate = round($succRate * 100, 2);
|
||||
$failRate = $failNum / $model->num;
|
||||
$failRate = round($failRate * 100, 2);
|
||||
}
|
||||
if ($model->num > $model->succ_num) {
|
||||
$succFailRate = $model->succ_num / ($model->num - $model->succ_num);
|
||||
$succFailRate = round($succFailRate * 100, 2);
|
||||
}
|
||||
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'reward_price' => floatval($model->reward_price),
|
||||
'reward_total' => floatval($model->reward_total),
|
||||
'succ_fail_rate' => $succFailRate,
|
||||
'fail_num' => $failNum,
|
||||
'succ_rate' => $succRate,
|
||||
'fail_rate' => $failRate,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\TaskModel;
|
||||
|
||||
class MessageReplyController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 关闭
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function close() {
|
||||
$devices = $this->request->param('devices');
|
||||
if (empty($devices) OR !is_array($devices)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
TaskModel::where(1)
|
||||
->whereIn('device_id', $devices)
|
||||
->where('platform', TaskModel::PLATFORM_XIANYU)
|
||||
->whereIn('status', [TaskModel::STATUS_AWAIT, TaskModel::STATUS_ALLOC])
|
||||
->where('is_deleted', TaskModel::IS_DELETED_NO)
|
||||
->update([
|
||||
'is_deleted' => TaskModel::IS_DELETED_YES,
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\ProductContentPoolModel;
|
||||
|
||||
class ProductContentPoolController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 获取关联数组
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function assoc() {
|
||||
return $this->jsonSucc(ProductContentPoolModel::assoc());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function save() {
|
||||
$contents = $this->request->param('contents');
|
||||
if (!is_array($contents)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
for ($i = 1; $i <= 6; $i ++) {
|
||||
if (isset($contents[$i])) {
|
||||
$content = trim($contents[$i]);
|
||||
$content = trim($content, '-');
|
||||
$content = trim($content);
|
||||
if (!empty($content)) {
|
||||
$model = ProductContentPoolModel::where(1)
|
||||
->where('number', $i)
|
||||
->find();
|
||||
if (empty($model)) {
|
||||
$model = new ProductContentPoolModel();
|
||||
}
|
||||
$model->number = $i;
|
||||
$model->content = trim($content);
|
||||
$model->save();
|
||||
} else {
|
||||
ProductContentPoolModel::where(1)
|
||||
->where('number', $i)
|
||||
->delete();
|
||||
}
|
||||
} else {
|
||||
ProductContentPoolModel::where(1)
|
||||
->where('number', $i)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\ProductGroupModel;
|
||||
use app\common\model\ProductModel;
|
||||
use think\db\Query;
|
||||
|
||||
class ProductController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$groupId = trim($this->request->param('group_id'));
|
||||
$isUsed = trim($this->request->param('is_used'));
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = ProductModel::where(1);
|
||||
if (strlen($groupId) > 0) {
|
||||
$query->where('group_id', $groupId);
|
||||
}
|
||||
if (isset(ProductModel::isUsedAssoc()[$isUsed])) {
|
||||
$query->where('is_used', $isUsed);
|
||||
}
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('title', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$images = [];
|
||||
foreach ($model->images as $image) {
|
||||
$images[] = array_merge($image, [
|
||||
'url' => $this->absoluteUrl($image['path']),
|
||||
]);
|
||||
}
|
||||
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'images' => $images,
|
||||
'is_used_name' => ProductModel::isUsedAssoc()[$model->is_used],
|
||||
'group_name' => isset(ProductGroupModel::assoc()[$model->group_id])
|
||||
? ProductGroupModel::assoc()[$model->group_id]
|
||||
: '',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
'groups' => $this->assocToList(ProductGroupModel::assoc()),
|
||||
'isUseds' => $this->assocToList(ProductModel::isUsedAssoc()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改主题
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function theme() {
|
||||
$checked = $this->request->param('checked');
|
||||
$themes = $this->request->param('themes');
|
||||
if (empty($checked)
|
||||
OR !is_array($checked)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
if (empty($themes) OR !is_array($themes)) {
|
||||
$themes = [];
|
||||
}
|
||||
|
||||
ProductModel::where(1)
|
||||
->whereIn('id', $checked)
|
||||
->update([
|
||||
'themes' => json_encode($themes, JSON_UNESCAPED_UNICODE),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改标签
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function label() {
|
||||
$checked = $this->request->param('checked');
|
||||
$labels = $this->request->param('labels');
|
||||
if (empty($checked)
|
||||
OR !is_array($checked)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
if (empty($labels) OR !is_array($labels)) {
|
||||
$labels = [];
|
||||
}
|
||||
|
||||
ProductModel::where(1)
|
||||
->whereIn('id', $checked)
|
||||
->update([
|
||||
'labels' => json_encode($labels, JSON_UNESCAPED_UNICODE),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改标题
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function title() {
|
||||
$checked = $this->request->param('checked');
|
||||
$type = intval($this->request->param('type'));
|
||||
$title = trim($this->request->param('title'));
|
||||
if (empty($checked)
|
||||
OR !is_array($checked)
|
||||
OR !in_array($type, [0, 1])
|
||||
OR empty($title)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
foreach (ProductModel::where(1)
|
||||
->whereIn('id', $checked)
|
||||
->select() as $model) {
|
||||
if ($type == 0) {
|
||||
$model->title = $model->title . $title;
|
||||
} else {
|
||||
$model->title = $title . $model->title;
|
||||
}
|
||||
$model->save();
|
||||
}
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改描述
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function content() {
|
||||
$checked = $this->request->param('checked');
|
||||
$type = intval($this->request->param('type'));
|
||||
$content = trim($this->request->param('content'));
|
||||
if (empty($checked)
|
||||
OR !is_array($checked)
|
||||
OR !in_array($type, [0, 1])
|
||||
OR empty($content)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
foreach (ProductModel::where(1)
|
||||
->whereIn('id', $checked)
|
||||
->select() as $model) {
|
||||
if ($type == 0) {
|
||||
$model->content = $model->content . "\n" . $content;
|
||||
} else {
|
||||
$model->content = $content . "\n" . $model->content;
|
||||
}
|
||||
$model->save();
|
||||
}
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改库存
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function stock() {
|
||||
$checked = $this->request->param('checked');
|
||||
$stock = intval($this->request->param('stock'));
|
||||
if (empty($checked)
|
||||
OR !is_array($checked)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
if ($stock <= 0) {
|
||||
return $this->jsonFail('库存不可小于1');
|
||||
}
|
||||
|
||||
ProductModel::where(1)
|
||||
->whereIn('id', $checked)
|
||||
->update([
|
||||
'stock' => $stock,
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改价格
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function price() {
|
||||
$checked = $this->request->param('checked');
|
||||
$type = intval($this->request->param('type'));
|
||||
$rate = floatval($this->request->param('rate'));
|
||||
$val = floatval($this->request->param('val'));
|
||||
$price = floatval($this->request->param('price'));
|
||||
if (empty($checked)
|
||||
OR !is_array($checked)
|
||||
OR !in_array($type, [0, 1, 2])) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
if ($type == 0 OR $type == 1) {
|
||||
if ($rate <= 0 AND $val <= 0) {
|
||||
return $this->jsonFail('请输入比例或数值');
|
||||
}
|
||||
} elseif ($type == 2) {
|
||||
if ($price <= 0) {
|
||||
return $this->jsonFail('请输入金额');
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ProductModel::where(1)
|
||||
->whereIn('id', $checked)
|
||||
->select() as $model) {
|
||||
if ($type == 0) {
|
||||
if ($rate > 0) {
|
||||
$model->price -= $model->price * $rate / 100;
|
||||
} else {
|
||||
$model->price -= $val;
|
||||
}
|
||||
} elseif ($type == 1) {
|
||||
if ($rate > 0) {
|
||||
$model->price += $model->price * $rate / 100;
|
||||
} else {
|
||||
$model->price += $val;
|
||||
}
|
||||
} elseif ($type == 2) {
|
||||
$model->price = $price;
|
||||
}
|
||||
$model->price = round($model->price, 2);
|
||||
if ($model->price > 0) {
|
||||
$model->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save() {
|
||||
$id = intval($this->request->param('id'));
|
||||
$groupId = intval($this->request->param('group_id'));
|
||||
$title = trim($this->request->param('title'));
|
||||
$content = trim($this->request->param('content'));
|
||||
$cb = $this->request->param('cb');
|
||||
$images = $this->request->param('images');
|
||||
$labels = $this->request->param('labels');
|
||||
$themes = $this->request->param('themes');
|
||||
$opts = $this->request->param('opts');
|
||||
$address = trim($this->request->param('address'));
|
||||
$price = floatval($this->request->param('price'));
|
||||
$stock = intval($this->request->param('stock'));
|
||||
$shippingFee = floatval($this->request->param('shipping_fee'));
|
||||
$video = trim($this->request->param('video'));
|
||||
if (empty($title)
|
||||
OR $groupId < 0
|
||||
OR empty($content)
|
||||
OR !is_array($cb)
|
||||
OR !is_array($images)
|
||||
OR empty($images)
|
||||
OR !is_array($labels)
|
||||
OR !is_array($themes)
|
||||
OR !is_array($opts)
|
||||
OR $price < 0
|
||||
OR $stock < 0
|
||||
OR $shippingFee < 0) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
foreach ($images as $i => $image) {
|
||||
unset($images[$i]['url']);
|
||||
}
|
||||
|
||||
if (!empty($id)) {
|
||||
$model = ProductModel::get($id);
|
||||
if (empty($model)) {
|
||||
return $this->jsonFail('对象未找到');
|
||||
}
|
||||
} else {
|
||||
$model = new ProductModel();
|
||||
}
|
||||
|
||||
$model->group_id = $groupId;
|
||||
$model->title = $title;
|
||||
$model->content = $content;
|
||||
$model->cb = $cb;
|
||||
$model->video = $video;
|
||||
$model->images = $images;
|
||||
$model->labels = $labels;
|
||||
$model->themes = $themes;
|
||||
$model->address = $address;
|
||||
$model->price = $price;
|
||||
$model->stock = $stock;
|
||||
$model->shipping_fee = $shippingFee;
|
||||
$model->opts = $opts;
|
||||
$model->save();
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete() {
|
||||
$id = intval($this->request->param('id'));
|
||||
if (empty($id)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
$model = ProductModel::get($id);
|
||||
if (empty($model)) {
|
||||
return $this->jsonFail('对象未找到');
|
||||
}
|
||||
|
||||
$model->delete();
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\CollectProductModel;
|
||||
use app\common\model\ProductGroupModel;
|
||||
use app\common\model\ProductModel;
|
||||
use think\db\Query;
|
||||
|
||||
class ProductGroupController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = ProductGroupModel::where(1);
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('name', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'product_num' => $model->productNum(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save() {
|
||||
$id = intval($this->request->param('id'));
|
||||
$name = trim($this->request->param('name'));
|
||||
if (empty($name)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
if (!empty($id)) {
|
||||
$model = ProductGroupModel::get($id);
|
||||
if (empty($model)) {
|
||||
return $this->jsonFail('对象未找到');
|
||||
}
|
||||
} else {
|
||||
$model = new ProductGroupModel();
|
||||
}
|
||||
|
||||
$model->name = $name;
|
||||
$model->save();
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return \think\response\Json
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete() {
|
||||
$id = intval($this->request->param('id'));
|
||||
if (empty($id)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
$model = ProductGroupModel::get($id);
|
||||
if (empty($model)) {
|
||||
return $this->jsonFail('对象未找到');
|
||||
}
|
||||
|
||||
CollectProductModel::where(1)
|
||||
->where('group_id', $model->id)
|
||||
->update([
|
||||
'group_id' => 0,
|
||||
]);
|
||||
ProductModel::where(1)
|
||||
->where('group_id', $model->id)
|
||||
->update([
|
||||
'group_id' => 0,
|
||||
]);
|
||||
|
||||
$model->delete();
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\ProductModel;
|
||||
use app\common\model\ProductUseModel;
|
||||
use app\common\model\TaskModel;
|
||||
use app\common\task\ProductReleaseTask;
|
||||
|
||||
class ProductReleaseController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save() {
|
||||
$devices = $this->request->param('devices');
|
||||
$params = ProductReleaseTask::params($this->request);
|
||||
if (empty($devices)
|
||||
OR !is_array($devices)
|
||||
OR is_null($params)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
foreach ($devices as $deviceId) {
|
||||
$pdts = [];
|
||||
for ($i = 0; $i < $params['release_num']; $i ++) {
|
||||
$product = array_shift($params['products']);
|
||||
if (!empty($product)) {
|
||||
$pdts[] = $product;
|
||||
}
|
||||
}
|
||||
if (!empty($pdts)) {
|
||||
$model = new TaskModel();
|
||||
$model->device_id = $deviceId;
|
||||
$model->platform = TaskModel::PLATFORM_XIANYU;
|
||||
$model->type = TaskModel::TYPE_PRODUCT_RELEASE;
|
||||
$model->params = array_merge($params, ['products' => $pdts]);
|
||||
$model->run_type = TaskModel::RUN_TYPE_ONCE;
|
||||
$model->run_time = '';
|
||||
if ($model->save()) {
|
||||
foreach ($pdts as $pdt) {
|
||||
$useModel = new ProductUseModel();
|
||||
$useModel->device_id = $deviceId;
|
||||
$useModel->release_id = $model->id;
|
||||
$useModel->product_id = $pdt['id'];
|
||||
$useModel->use_type = $model->platform;
|
||||
$useModel->save();
|
||||
|
||||
ProductModel::where(1)
|
||||
->where('id', $pdt['id'])
|
||||
->update([
|
||||
'is_used' => ProductModel::IS_USED_YES,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
}
|
||||
@@ -1,795 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\CloneInfo;
|
||||
use app\common\model\MemberModel;
|
||||
use app\common\model\MemberMoneyModel;
|
||||
use app\common\model\MemberQrcodeModel;
|
||||
use app\common\model\MemberWithdrawModel;
|
||||
use app\common\model\StatisticsModel;
|
||||
use app\common\Utils;
|
||||
|
||||
class StatController extends BaseLoginController {
|
||||
|
||||
protected $chartColumns = [
|
||||
'register' => '用户注册',
|
||||
'qr_number' => '扫码企点号',
|
||||
'qr_member' => '扫码人数',
|
||||
'qrcode' => '取码次数',
|
||||
'qrcode_succ' => '取码成功',
|
||||
'clone_succ' => '克隆成功',
|
||||
'clone_error' => '克隆错误',
|
||||
'clone_timeout' => '克隆超时',
|
||||
'xinyue_select' => '选择克隆心悦数',
|
||||
'xinyue_clone' => '克隆成功心悦数',
|
||||
'reward_clone' => '克隆奖励笔数',
|
||||
'reward_invite1' => '一级奖励笔数',
|
||||
'reward_invite2' => '二级奖励笔数',
|
||||
'money_clone' => '克隆奖励金额',
|
||||
'money_invite1' => '一级奖励金额',
|
||||
'money_invite2' => '二级奖励金额',
|
||||
'withdraw' => '申请提现笔数',
|
||||
'withdraw_check' => '检测通过笔数',
|
||||
'withdraw_money' => '申请提现金额',
|
||||
'withdraw_money_check' => '检测通过金额',
|
||||
'withdraw_succ' => '提现成功笔数',
|
||||
'withdraw_succ_money' => '提现成功金额',
|
||||
'withdraw_fail' => '提现失败笔数',
|
||||
'withdraw_fail_money' => '提现失败金额',
|
||||
];
|
||||
|
||||
public function dayChartView() {
|
||||
$day = trim($this->request->param('day'));
|
||||
$columns = trim($this->request->param('columns'));
|
||||
$columns = explode('|', $columns);
|
||||
if (empty($day) OR empty($columns)) {
|
||||
exit('Page not found.');
|
||||
}
|
||||
|
||||
$legend = [];
|
||||
$xAxis = [];
|
||||
$series = [];
|
||||
for ($i = 0; $i <= 23; $i ++) {
|
||||
$xAxis[] = $i . '时';
|
||||
}
|
||||
foreach ($columns as $i => $column) {
|
||||
if (isset($this->chartColumns[$column])) {
|
||||
$legend[] = $this->chartColumns[$column];
|
||||
$series[$column] = [
|
||||
'name' => $this->chartColumns[$column],
|
||||
'type' => 'line',
|
||||
'stack' => 'Total',
|
||||
'data' => [],
|
||||
];
|
||||
} else {
|
||||
unset($columns[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$list = $this->getDayData($day);
|
||||
$rows = [];
|
||||
foreach ($list as $row) {
|
||||
$rows[$row['hour']] = $row;
|
||||
}
|
||||
|
||||
foreach ($xAxis as $axi) {
|
||||
if (isset($rows[$axi])) {
|
||||
foreach ($columns as $column) {
|
||||
$series[$column]['data'][] = $rows[$axi][$column];
|
||||
}
|
||||
} else {
|
||||
foreach ($columns as $column) {
|
||||
$series[$column]['data'][] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fetch('/chart-day', [
|
||||
'legend' => $legend,
|
||||
'xAxis' => $xAxis,
|
||||
'series' => array_values($series),
|
||||
]);
|
||||
}
|
||||
|
||||
public function monthChartView() {
|
||||
$month = trim($this->request->param('month'));
|
||||
$columns = trim($this->request->param('columns'));
|
||||
$columns = explode('|', $columns);
|
||||
if (empty($month) OR empty($columns)) {
|
||||
exit('Page not found.');
|
||||
}
|
||||
|
||||
$time = strtotime($month . '01');
|
||||
if ($time === FALSE) {
|
||||
exit('Page not found.');
|
||||
}
|
||||
|
||||
$days = date('t', $time);
|
||||
$legend = [];
|
||||
$xAxis = [];
|
||||
$series = [];
|
||||
for ($i = 1; $i <= $days; $i ++) {
|
||||
$xAxis[] = $i . '日';
|
||||
}
|
||||
foreach ($columns as $i => $column) {
|
||||
if (isset($this->chartColumns[$column])) {
|
||||
$legend[] = $this->chartColumns[$column];
|
||||
$series[$column] = [
|
||||
'name' => $this->chartColumns[$column],
|
||||
'type' => 'line',
|
||||
'stack' => 'Total',
|
||||
'data' => [],
|
||||
];
|
||||
} else {
|
||||
unset($columns[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$list = $this->getMonthData($month);
|
||||
$rows = [];
|
||||
foreach ($list as $row) {
|
||||
$row['day'] = str_replace(['年', '月'], '-', $row['day']);
|
||||
$row['day'] = str_replace(['日'], '', $row['day']);
|
||||
$row['day'] = intval(date('d', strtotime($row['day']))) . '日';
|
||||
|
||||
$rows[$row['day']] = $row;
|
||||
}
|
||||
|
||||
foreach ($xAxis as $axi) {
|
||||
if (isset($rows[$axi])) {
|
||||
foreach ($columns as $column) {
|
||||
$series[$column]['data'][] = $rows[$axi][$column];
|
||||
}
|
||||
} else {
|
||||
foreach ($columns as $column) {
|
||||
$series[$column]['data'][] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fetch('/chart-day', [
|
||||
'legend' => $legend,
|
||||
'xAxis' => $xAxis,
|
||||
'series' => array_values($series),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日统计图表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function dayChart() {
|
||||
$day = trim($this->request->param('day'));
|
||||
$columns = $this->request->param('columns');
|
||||
if (empty($day)) {
|
||||
$day = date('Y-m-d');
|
||||
}
|
||||
if (strtotime($day) === FALSE) {
|
||||
return $this->jsonFail('日期错误');
|
||||
}
|
||||
if (empty($columns)) {
|
||||
$columns = ['xinyue_select', 'xinyue_clone', 'money_clone'];
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'day' => $day,
|
||||
'columns' => $columns,
|
||||
'chartColumns' => $this->assocToList($this->chartColumns),
|
||||
'url' => $this->absoluteUrl('/backend/stat/dayChartView?token=' . $this->token . '&day=' . $day . '&columns=' . implode('|', $columns)),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 月统计图表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function monthChart() {
|
||||
$month = trim($this->request->param('month'));
|
||||
$columns = $this->request->param('columns');
|
||||
if (empty($month)) {
|
||||
$month = date('Ym');
|
||||
}
|
||||
if (empty($columns)) {
|
||||
$columns = ['xinyue_select', 'xinyue_clone', 'money_clone'];
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'month' => $month,
|
||||
'months' => $this->assocToList($this->getMonths()),
|
||||
'columns' => $columns,
|
||||
'chartColumns' => $this->assocToList($this->chartColumns),
|
||||
'url' => $this->absoluteUrl('/backend/stat/monthChartView?token=' . $this->token . '&month=' . $month . '&columns=' . implode('|', $columns)),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日统计列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function dayIndex() {
|
||||
$day = trim($this->request->param('day'));
|
||||
if (empty($day)) {
|
||||
$day = date('Y-m-d');
|
||||
}
|
||||
if (strtotime($day) === FALSE) {
|
||||
return $this->jsonFail('日期错误');
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $this->getDayData($day),
|
||||
'day' => $day,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 月统计
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function monthIndex() {
|
||||
$month = trim($this->request->param('month'));
|
||||
if (empty($month)) {
|
||||
$month = date('Ym');
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $this->getMonthData($month),
|
||||
'month' => $month,
|
||||
'months' => $this->assocToList($this->getMonths()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function get() {
|
||||
$weekTime = time() - 7 * 24 * 3600;
|
||||
$monthTime = time() - 30 * 24 * 3600;
|
||||
|
||||
$memberTotal = MemberModel::where(1)
|
||||
->count();
|
||||
$memberToday = MemberModel::where(1)
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')))
|
||||
->count();
|
||||
$memberYesterday = MemberModel::where(1)
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')) - 24 * 3600)
|
||||
->where('create_time', '<', strtotime(date('Y-m-d')))
|
||||
->count();
|
||||
$memberWeek = MemberModel::where(1)
|
||||
->where('create_time', '>=', $weekTime)
|
||||
->count();
|
||||
$memberMonth = MemberModel::where(1)
|
||||
->where('create_time', '>=', $monthTime)
|
||||
->count();
|
||||
|
||||
/*$xinyueTotal = MemberQrcodeModel::where(1)
|
||||
->where('status', MemberQrcodeModel::STATUS_SUCCESS)
|
||||
->sum('select_xinyue_count');
|
||||
$xinyueToday = MemberQrcodeModel::where(1)
|
||||
->where('status', MemberQrcodeModel::STATUS_SUCCESS)
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')))
|
||||
->sum('select_xinyue_count');
|
||||
$xinyueYesterday = MemberQrcodeModel::where(1)
|
||||
->where('status', MemberQrcodeModel::STATUS_SUCCESS)
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')) - 24 * 3600)
|
||||
->where('create_time', '<', strtotime(date('Y-m-d')))
|
||||
->sum('select_xinyue_count');
|
||||
$xinyueWeek = MemberQrcodeModel::where(1)
|
||||
->where('status', MemberQrcodeModel::STATUS_SUCCESS)
|
||||
->where('create_time', '>=', $weekTime)
|
||||
->sum('select_xinyue_count');
|
||||
$xinyueMonth = MemberQrcodeModel::where(1)
|
||||
->where('status', MemberQrcodeModel::STATUS_SUCCESS)
|
||||
->where('create_time', '>=', $monthTime)
|
||||
->sum('select_xinyue_count');*/
|
||||
|
||||
$xinyueTotal = StatisticsModel::where(1)
|
||||
->sum('xinyue_select');
|
||||
$xinyueToday = StatisticsModel::where(1)
|
||||
->where('day', date('Ymd'))
|
||||
->sum('xinyue_select');
|
||||
$xinyueYesterday = StatisticsModel::where(1)
|
||||
->where('day', date('Ymd', time() - 24 * 3600))
|
||||
->sum('xinyue_select');
|
||||
$xinyueWeek = StatisticsModel::where(1)
|
||||
->where('day', '>=', date('Ymd', time() - 7 * 24 * 3600))
|
||||
->sum('xinyue_select');
|
||||
$xinyueMonth = StatisticsModel::where(1)
|
||||
->where('day', '>=', date('Ymd', time() - 30 * 24 * 3600))
|
||||
->sum('xinyue_select');
|
||||
|
||||
$moneyTotal = MemberMoneyModel::where(1)
|
||||
->where('money', '>', 0)
|
||||
->where('status', MemberMoneyModel::STATUS_SUCC)
|
||||
->sum('money');
|
||||
$moneyToday = MemberMoneyModel::where(1)
|
||||
->where('money', '>', 0)
|
||||
->where('status', MemberMoneyModel::STATUS_SUCC)
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')))
|
||||
->sum('money');
|
||||
$moneyYesterday = MemberMoneyModel::where(1)
|
||||
->where('money', '>', 0)
|
||||
->where('status', MemberMoneyModel::STATUS_SUCC)
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')) - 24 * 3600)
|
||||
->where('create_time', '<', strtotime(date('Y-m-d')))
|
||||
->sum('money');
|
||||
$moneyWeek = MemberMoneyModel::where(1)
|
||||
->where('money', '>', 0)
|
||||
->where('status', MemberMoneyModel::STATUS_SUCC)
|
||||
->where('create_time', '>=', $weekTime)
|
||||
->sum('money');
|
||||
$moneyMonth = MemberMoneyModel::where(1)
|
||||
->where('money', '>', 0)
|
||||
->where('status', MemberMoneyModel::STATUS_SUCC)
|
||||
->where('create_time', '>=', $monthTime)
|
||||
->sum('money');
|
||||
|
||||
$withdrawTotal = MemberMoneyModel::where(1)
|
||||
->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberMoneyModel::STATUS_SUCC, MemberMoneyModel::STATUS_AWAIT])
|
||||
->sum('money');
|
||||
$withdrawToday = MemberMoneyModel::where(1)
|
||||
->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberMoneyModel::STATUS_SUCC, MemberMoneyModel::STATUS_AWAIT])
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')))
|
||||
->sum('money');
|
||||
$withdrawYesterday = MemberMoneyModel::where(1)
|
||||
->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberMoneyModel::STATUS_SUCC, MemberMoneyModel::STATUS_AWAIT])
|
||||
->where('create_time', '>=', strtotime(date('Y-m-d')) - 24 * 3600)
|
||||
->where('create_time', '<', strtotime(date('Y-m-d')))
|
||||
->sum('money');
|
||||
$withdrawWeek = MemberMoneyModel::where(1)
|
||||
->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberMoneyModel::STATUS_SUCC, MemberMoneyModel::STATUS_AWAIT])
|
||||
->where('create_time', '>=', $weekTime)
|
||||
->sum('money');
|
||||
$withdrawMonth = MemberMoneyModel::where(1)
|
||||
->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberMoneyModel::STATUS_SUCC, MemberMoneyModel::STATUS_AWAIT])
|
||||
->where('create_time', '>=', $monthTime)
|
||||
->sum('money');
|
||||
|
||||
$remitTotal = MemberWithdrawModel::where(1)
|
||||
//->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberWithdrawModel::STATUS_SUCC, MemberWithdrawModel::STATUS_AUTO_SUCC])
|
||||
->sum('money');
|
||||
$remitToday = MemberWithdrawModel::where(1)
|
||||
//->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberWithdrawModel::STATUS_SUCC, MemberWithdrawModel::STATUS_AUTO_SUCC])
|
||||
->where('verify_time', '>=', strtotime(date('Y-m-d')))
|
||||
->sum('money');
|
||||
$remitYesterday = MemberWithdrawModel::where(1)
|
||||
//->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberWithdrawModel::STATUS_SUCC, MemberWithdrawModel::STATUS_AUTO_SUCC])
|
||||
->where('verify_time', '>=', strtotime(date('Y-m-d')) - 24 * 3600)
|
||||
->where('verify_time', '<', strtotime(date('Y-m-d')))
|
||||
->sum('money');
|
||||
$remitWeek = MemberWithdrawModel::where(1)
|
||||
//->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberWithdrawModel::STATUS_SUCC, MemberWithdrawModel::STATUS_AUTO_SUCC])
|
||||
->where('verify_time', '>=', $weekTime)
|
||||
->sum('money');
|
||||
$remitMonth = MemberWithdrawModel::where(1)
|
||||
//->whereIn('type', MemberMoneyModel::withdrawTypes())
|
||||
->whereIn('status', [MemberWithdrawModel::STATUS_SUCC, MemberWithdrawModel::STATUS_AUTO_SUCC])
|
||||
->where('verify_time', '>=', $monthTime)
|
||||
->sum('money');
|
||||
|
||||
return $this->jsonSucc([
|
||||
'member_total' => $memberTotal,
|
||||
'member_today' => $memberToday,
|
||||
'member_yesterday' => $memberYesterday,
|
||||
'member_week' => $memberWeek,
|
||||
'member_month' => $memberMonth,
|
||||
'xinyue_total' => $xinyueTotal,
|
||||
'xinyue_today' => $xinyueToday,
|
||||
'xinyue_yesterday' => $xinyueYesterday,
|
||||
'xinyue_week' => $xinyueWeek,
|
||||
'xinyue_month' => $xinyueMonth,
|
||||
'money_total' => $moneyTotal,
|
||||
'money_today' => $moneyToday,
|
||||
'money_yesterday' => $moneyYesterday,
|
||||
'money_week' => $moneyWeek,
|
||||
'money_month' => $moneyMonth,
|
||||
'withdraw' => round($moneyTotal + $withdrawTotal, 2),
|
||||
'withdraw_total' => abs($withdrawTotal),
|
||||
'withdraw_today' => abs($withdrawToday),
|
||||
'withdraw_yesterday' => abs($withdrawYesterday),
|
||||
'withdraw_week' => abs($withdrawWeek),
|
||||
'withdraw_month' => abs($withdrawMonth),
|
||||
'remit_total' => abs($remitTotal),
|
||||
'remit_today' => abs($remitToday),
|
||||
'remit_yesterday' => abs($remitYesterday),
|
||||
'remit_week' => abs($remitWeek),
|
||||
'remit_month' => abs($remitMonth),
|
||||
'clone_total' => 'Loading...',
|
||||
'clone_today' => '-',
|
||||
'clone_today_nr' => '-',
|
||||
'clone_today_r' => '-',
|
||||
'clone_yesterday' => '-',
|
||||
'clone_week' => '-',
|
||||
'clone_month' => '-',
|
||||
'clone_today_pay' => '-',
|
||||
'clone_today_unpay' => '-',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取克隆统计信息
|
||||
*
|
||||
* @return \think\response\Json
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function getClone() {
|
||||
$cloneTotal = StatisticsModel::where(1)
|
||||
->sum('xinyue_clone');
|
||||
$cloneToday = StatisticsModel::where(1)
|
||||
->where('day', date('Ymd'))
|
||||
->sum('xinyue_clone');
|
||||
$cloneTodayNr = StatisticsModel::where(1)
|
||||
->where('day', date('Ymd'))
|
||||
->sum('xinyue_clone_nr');
|
||||
$cloneYesterday = StatisticsModel::where(1)
|
||||
->where('day', date('Ymd', time() - 24 * 3600))
|
||||
->sum('xinyue_clone');
|
||||
$cloneWeek = StatisticsModel::where(1)
|
||||
->where('day', '>=', date('Ymd', time() - 7 * 24 * 3600))
|
||||
->sum('xinyue_clone');
|
||||
$cloneMonth = StatisticsModel::where(1)
|
||||
->where('day', '>=', date('Ymd', time() - 30 * 24 * 3600))
|
||||
->sum('xinyue_clone');
|
||||
$cloneTodayUnpay = CloneInfo::getCloneSuccUnpayByTime(strtotime(date('Ymd')), time());
|
||||
$cloneTodayPay = CloneInfo::getCloneSuccPayByTime(strtotime(date('Ymd')), time());
|
||||
|
||||
return $this->jsonSucc([
|
||||
'clone_total' => $cloneTotal,
|
||||
'clone_today' => $cloneToday,
|
||||
'clone_today_nr' => $cloneTodayNr,
|
||||
'clone_today_r' => $cloneToday - $cloneTodayNr,
|
||||
'clone_yesterday' => $cloneYesterday,
|
||||
'clone_week' => $cloneWeek,
|
||||
'clone_month' => $cloneMonth,
|
||||
'clone_today_pay' => $cloneTodayPay,
|
||||
'clone_today_unpay' => $cloneTodayUnpay,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日统计数据
|
||||
*
|
||||
* @param $day
|
||||
* @return array
|
||||
*/
|
||||
protected function getDayData($day) {
|
||||
$list = [];
|
||||
foreach (StatisticsModel::where(1)
|
||||
->where('day', date('Ymd', strtotime($day)))
|
||||
->order('hour', 'DESC')
|
||||
->select() as $model) {
|
||||
$xinyueClone = $model->xinyue_clone;
|
||||
$moneyTotal = $model->money_clone
|
||||
+ $model->money_invite1
|
||||
+ $model->money_invite2
|
||||
+ $model->money_jm
|
||||
+ $model->money_point;
|
||||
|
||||
$scan1CloneCount = $model->scan1_clone_count;
|
||||
$scan2CloneCount = $model->scan2_clone_count;
|
||||
$scan3CloneCount = $model->scan3_clone_count;
|
||||
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'day' => date('Y年m月d日', strtotime($model->day)),
|
||||
'hour' => $model->hour . '时',
|
||||
'reward_price' => $xinyueClone > 0 ? round($moneyTotal / $xinyueClone, 2) : 0,
|
||||
'scan1_price' => $scan1CloneCount > 0 ? round($model->scan1_money / $scan1CloneCount, 2) : 0,
|
||||
'scan2_price' => $scan2CloneCount > 0 ? round($model->scan2_money / $scan2CloneCount, 2) : 0,
|
||||
'scan3_price' => $scan3CloneCount > 0 ? round($model->scan3_money / $scan3CloneCount, 2) : 0,
|
||||
]);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月份
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getMonths() {
|
||||
$begin = 20240301;
|
||||
$months = [];
|
||||
$nowTime = time();
|
||||
while ($nowTime >= strtotime($begin)) {
|
||||
if (!isset($months[date('Ym', $nowTime)])) {
|
||||
$months[date('Ym', $nowTime)] = date('Y年m月', $nowTime);
|
||||
}
|
||||
|
||||
$nowTime -= 24 * 3600;
|
||||
}
|
||||
return $months;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月统计数据
|
||||
*
|
||||
* @param $month
|
||||
* @return array|\think\response\Json
|
||||
*/
|
||||
protected function getMonthData($month) {
|
||||
$time = strtotime($month . '01');
|
||||
if ($time === FALSE) {
|
||||
return $this->jsonFail('月份错误');
|
||||
}
|
||||
|
||||
$today = date('Ymd');
|
||||
$days = [];
|
||||
for ($i = date('t', $time); $i >= 1; $i --) {
|
||||
$day = date('Ymd', strtotime(date('Y', $time) . '-' . date('m', $time) . '-' . $i));
|
||||
if ($day <= $today) {
|
||||
$days[] = $day;
|
||||
}
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($days as $day) {
|
||||
$row = [
|
||||
'day' => date('Y年m月d日', strtotime($day)),
|
||||
'register' => 0,
|
||||
'qr_number' => 0,
|
||||
'qr_member' => 0,
|
||||
'qrcode' => 0,
|
||||
'qrcode_succ' => 0,
|
||||
'clone_succ' => 0,
|
||||
'clone_error' => 0,
|
||||
'clone_timeout' => 0,
|
||||
'xinyue_select' => 0,
|
||||
'xinyue_clone' => 0,
|
||||
'reward_clone' => 0,
|
||||
'reward_invite1' => 0,
|
||||
'reward_invite2' => 0,
|
||||
'money_clone' => 0,
|
||||
'money_invite1' => 0,
|
||||
'money_invite2' => 0,
|
||||
'money_jm' => 0,
|
||||
'money_point' => 0,
|
||||
'withdraw' => 0,
|
||||
'withdraw_check' => 0,
|
||||
'withdraw_money' => 0,
|
||||
'withdraw_money_check' => 0,
|
||||
'withdraw_succ' => 0,
|
||||
'withdraw_succ_money' => 0,
|
||||
'withdraw_fail' => 0,
|
||||
'withdraw_fail_money' => 0,
|
||||
'scan1_num' => 0,
|
||||
'scan2_num' => 0,
|
||||
'scan3_num' => 0,
|
||||
'scan1_xinyue_count' => 0,
|
||||
'scan2_xinyue_count' => 0,
|
||||
'scan3_xinyue_count' => 0,
|
||||
'scan1_clone_count' => 0,
|
||||
'scan2_clone_count' => 0,
|
||||
'scan3_clone_count' => 0,
|
||||
'scan1_money' => 0,
|
||||
'scan2_money' => 0,
|
||||
'scan3_money' => 0,
|
||||
'scan1_price' => 0,
|
||||
'scan2_price' => 0,
|
||||
'scan3_price' => 0,
|
||||
'first_settlement_count' => 0,
|
||||
'repeat_settlement_count' => 0,
|
||||
'first_reward_money' => 0,
|
||||
'repeat_reward_money' => 0,
|
||||
'first_clone_succ' => 0,
|
||||
'repeat_clone_succ' => 0,
|
||||
'first_price' => 0,
|
||||
'repeat_price' => 0,
|
||||
];
|
||||
|
||||
foreach (StatisticsModel::where(1)
|
||||
->where('day', $day)
|
||||
->select() as $model) {
|
||||
$row['register'] += $model->register;
|
||||
$row['qr_number'] += $model->qr_number;
|
||||
$row['qr_member'] += $model->qr_member;
|
||||
$row['qrcode'] += $model->qrcode;
|
||||
$row['qrcode_succ'] += $model->qrcode_succ;
|
||||
$row['clone_succ'] += $model->clone_succ;
|
||||
$row['clone_error'] += $model->clone_error;
|
||||
$row['clone_timeout'] += $model->clone_timeout;
|
||||
$row['xinyue_select'] += $model->xinyue_select;
|
||||
$row['xinyue_clone'] += $model->xinyue_clone;
|
||||
$row['reward_clone'] += $model->reward_clone;
|
||||
$row['reward_invite1'] += $model->reward_invite1;
|
||||
$row['reward_invite2'] += $model->reward_invite2;
|
||||
$row['money_clone'] += $model->money_clone;
|
||||
$row['money_invite1'] += $model->money_invite1;
|
||||
$row['money_invite2'] += $model->money_invite2;
|
||||
$row['money_jm'] += $model->money_jm;
|
||||
$row['money_point'] += $model->money_point;
|
||||
$row['withdraw'] += $model->withdraw;
|
||||
$row['withdraw_check'] += $model->withdraw_check;
|
||||
$row['withdraw_money'] += $model->withdraw_money;
|
||||
$row['withdraw_money_check'] += $model->withdraw_money_check;
|
||||
$row['withdraw_succ'] += $model->withdraw_succ;
|
||||
$row['withdraw_succ_money'] += $model->withdraw_succ_money;
|
||||
$row['withdraw_fail'] += $model->withdraw_fail;
|
||||
$row['withdraw_fail_money'] += $model->withdraw_fail_money;
|
||||
|
||||
$row['first_settlement_count'] += $model->first_settlement_count;
|
||||
$row['repeat_settlement_count'] += $model->repeat_settlement_count;
|
||||
$row['first_reward_money'] += $model->first_reward_money;
|
||||
$row['repeat_reward_money'] += $model->repeat_reward_money;
|
||||
$row['first_clone_succ'] += $model->first_clone_succ;
|
||||
$row['repeat_clone_succ'] += $model->repeat_clone_succ;
|
||||
/*$row['scan1_num'] += $model->scan1_num;
|
||||
$row['scan2_num'] += $model->scan2_num;
|
||||
$row['scan3_num'] += $model->scan3_num;
|
||||
$row['scan1_xinyue_count'] += $model->scan1_xinyue_count;
|
||||
$row['scan2_xinyue_count'] += $model->scan2_xinyue_count;
|
||||
$row['scan3_xinyue_count'] += $model->scan3_xinyue_count;
|
||||
$row['scan1_clone_count'] += $model->scan1_clone_count;
|
||||
$row['scan2_clone_count'] += $model->scan2_clone_count;
|
||||
$row['scan3_clone_count'] += $model->scan3_clone_count;
|
||||
$row['scan1_money'] += $model->scan1_money;
|
||||
$row['scan2_money'] += $model->scan2_money;
|
||||
$row['scan3_money'] += $model->scan3_money;*/
|
||||
}
|
||||
|
||||
$xinyueClone = $row['xinyue_clone'];
|
||||
$moneyTotal = $row['money_clone']
|
||||
+ $row['money_invite1']
|
||||
+ $row['money_invite2']
|
||||
+ $row['money_jm']
|
||||
+ $row['money_point'];
|
||||
|
||||
//$scan1CloneCount = $row['scan1_clone_count'];
|
||||
//$scan2CloneCount = $row['scan2_clone_count'];
|
||||
//$scan3CloneCount = $row['scan3_clone_count'];
|
||||
|
||||
$row['reward_price'] = $xinyueClone > 0 ? round($moneyTotal / $xinyueClone, 2) : 0;
|
||||
//$row['scan1_price'] = $scan1CloneCount > 0 ? round($row['scan1_money'] / $scan1CloneCount, 2) : 0;
|
||||
//$row['scan2_price'] = $scan2CloneCount > 0 ? round($row['scan2_money'] / $scan2CloneCount, 2) : 0;
|
||||
//$row['scan3_price'] = $scan3CloneCount > 0 ? round($row['scan3_money'] / $scan3CloneCount, 2) : 0;
|
||||
|
||||
$row['first_reward_money'] = round($row['first_reward_money'], 2);
|
||||
$row['repeat_reward_money'] = round($row['repeat_reward_money'], 2);
|
||||
if ($row['first_clone_succ'] > 0) {
|
||||
$row['first_price'] = round($row['first_reward_money'] / $row['first_clone_succ'], 2);
|
||||
}
|
||||
if ($row['repeat_clone_succ'] > 0) {
|
||||
$row['repeat_price'] = round($row['repeat_reward_money'] / $row['repeat_clone_succ'], 2);
|
||||
}
|
||||
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function scan() {
|
||||
$time = time();// - 24 * 3600;
|
||||
$ids0 = MemberQrcodeModel::where(1)
|
||||
->field('id')
|
||||
->where('create_time', '>=', strtotime(date('Ymd', $time)))
|
||||
->where('status', MemberQrcodeModel::STATUS_SUCCESS)
|
||||
->where('settlement_count1', '>', 0)
|
||||
->select()
|
||||
->column('id');
|
||||
$ids1 = MemberQrcodeModel::where(1)
|
||||
->field('id')
|
||||
->where('create_time', '>=', strtotime(date('Ymd', $time)))
|
||||
->where('status', MemberQrcodeModel::STATUS_SUCCESS)
|
||||
->where('settlement_count2', '>', 0)
|
||||
->select()
|
||||
->column('id');
|
||||
|
||||
$money0 = 0;
|
||||
$price0 = 0;
|
||||
$xinyue0 = 0;
|
||||
$cloneSucc0 = 0;
|
||||
$cloneFailRate0 = 0;
|
||||
if (!empty($ids0)) {
|
||||
$money0 = MemberQrcodeModel::where(1)
|
||||
->field('reward_price * reward_count AS reward_money0')
|
||||
->whereIn('id', $ids0)
|
||||
->select()
|
||||
->reduce(function ($money, $item) {
|
||||
return $money + $item['reward_money0'];
|
||||
});
|
||||
$xinyue0 = MemberQrcodeModel::where(1)
|
||||
->whereIn('id', $ids0)
|
||||
->sum('settlement_count1');
|
||||
$qrIds0 = MemberQrcodeModel::where(1)
|
||||
->field('qr_id')
|
||||
->whereIn('id', $ids0)
|
||||
->where('qr_id', '>', 0)
|
||||
->select()
|
||||
->column('qr_id');
|
||||
if (!empty($qrIds0)) {
|
||||
$cloneSucc0 = CloneInfo::getCloneSuccCount1ByQrIds($qrIds0);
|
||||
}
|
||||
|
||||
if ($xinyue0 > 0) {
|
||||
$cloneFailRate0 = round(($xinyue0 - $cloneSucc0) / $xinyue0, 4) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
$money1 = 0;
|
||||
$price1 = 0;
|
||||
$xinyue1 = 0;
|
||||
$cloneSucc1 = 0;
|
||||
$cloneFailRate1 = 0;
|
||||
if (!empty($ids1)) {
|
||||
$money1 = MemberQrcodeModel::where(1)
|
||||
->field('reward_price2 * reward_count2 AS reward_money0')
|
||||
->whereIn('id', $ids1)
|
||||
->select()
|
||||
->reduce(function ($money, $item) {
|
||||
return $money + $item['reward_money0'];
|
||||
});
|
||||
$xinyue1 = MemberQrcodeModel::where(1)
|
||||
->whereIn('id', $ids1)
|
||||
->sum('settlement_count2');
|
||||
if ($cloneSucc1 > 0) {
|
||||
$price1 = floatval(round($money1 / $cloneSucc1, 2));
|
||||
}
|
||||
|
||||
$qrIds1 = MemberQrcodeModel::where(1)
|
||||
->field('qr_id')
|
||||
->whereIn('id', $ids1)
|
||||
->where('qr_id', '>', 0)
|
||||
->select()
|
||||
->column('qr_id');
|
||||
if (!empty($qrIds1)) {
|
||||
$cloneSucc1 = CloneInfo::getCloneSuccCount2ByQrIds($qrIds1);
|
||||
}
|
||||
|
||||
if ($xinyue1 > 0) {
|
||||
$cloneFailRate1 = round(($xinyue1 - $cloneSucc1) / $xinyue1, 4) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
$total = floatval(MemberMoneyModel::where(1)
|
||||
->where('create_time', '>=', strtotime(date('Ymd', $time)))
|
||||
->whereIn('type', MemberMoneyModel::rewardTypes())
|
||||
->sum('money'));
|
||||
|
||||
Utils::allocNumber($total, $money0, $money1);
|
||||
|
||||
if ($cloneSucc0 > 0) {
|
||||
$price0 = floatval(round($money0 / $cloneSucc0, 2));
|
||||
}
|
||||
if ($cloneSucc1 > 0) {
|
||||
$price1 = floatval(round($money1 / $cloneSucc1, 2));
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'scan0' => [
|
||||
'num' => count($ids0),
|
||||
'money' => floatval(round($money0, 2)),
|
||||
'price' => $price0,
|
||||
'xinyue' => $xinyue0,
|
||||
'clone_succ' => $cloneSucc0,
|
||||
'rate_clone_fail' => $cloneFailRate0,
|
||||
],
|
||||
'scan1' => [
|
||||
'num' => count($ids1),
|
||||
'money' => floatval(round($money1, 2)),
|
||||
'price' => $price1,
|
||||
'xinyue' => $xinyue1,
|
||||
'clone_succ' => $cloneSucc1,
|
||||
'rate_clone_fail' => $cloneFailRate1,
|
||||
],
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\DeviceModel;
|
||||
use app\common\model\LogModel;
|
||||
use app\common\model\TaskDetailModel;
|
||||
use app\common\model\TaskModel;
|
||||
|
||||
class TaskController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$deviceId = intval($this->request->param('device_id'));
|
||||
$type = trim($this->request->param('type'));
|
||||
$runType = trim($this->request->param('run_type'));
|
||||
$status = trim($this->request->param('status'));
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = TaskModel::where(1)
|
||||
->alias('t1')
|
||||
->field('t1.*')
|
||||
->leftJoin(
|
||||
DeviceModel::where(1)->getTable() . ' t2',
|
||||
't2.id = t1.device_id')
|
||||
->where('t1.is_deleted', TaskModel::IS_DELETED_NO);
|
||||
if (!empty($deviceId)) {
|
||||
$query->where('t1.device_id', $deviceId);
|
||||
}
|
||||
if (isset(TaskModel::typeAssoc()[$type])) {
|
||||
$query->where('t1.type', $type);
|
||||
}
|
||||
if (isset(TaskModel::statusAssoc()[$status])) {
|
||||
$query->where('t1.status', $status);
|
||||
}
|
||||
if (isset(TaskModel::runTypeAssoc()[$runType])) {
|
||||
$query->where('t1.run_type', $runType);
|
||||
}
|
||||
if (!empty($keywords)) {
|
||||
$query->whereLike('t2.number', '%' . $keywords . '%');
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('t1.id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$device = $model->device();
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'device_number' => $device ? $device->number : '',
|
||||
'device_name' => $device ? $device->name : '',
|
||||
'device_online' => ($device AND $device->isOnline()) ? '在线' : '离线',
|
||||
'type_name' => TaskModel::typeAssoc()[$model->type],
|
||||
'run_type_name' => TaskModel::runTypeAssoc()[$model->run_type],
|
||||
'status_name' => TaskModel::statusAssoc()[$model->status],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
'platforms' => $this->assocToList(TaskModel::platformAssoc()),
|
||||
'types' => $this->assocToList(TaskModel::typeAssoc()),
|
||||
'runTypes' => $this->assocToList(TaskModel::runTypeAssoc()),
|
||||
'statuses' => $this->assocToList(TaskModel::statusAssoc()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function log() {
|
||||
$id = intval($this->request->param('id'));
|
||||
if (empty($id)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
$detail = TaskDetailModel::where(1)
|
||||
->where('task_id', $id)
|
||||
->order('id', 'DESC')
|
||||
->find();
|
||||
if (empty($detail)) {
|
||||
return $this->jsonSucc([]);
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach (LogModel::where(1)
|
||||
->where('task_id', $detail->id)
|
||||
->order('id', 'ASC')
|
||||
->select() as $model) {
|
||||
$list[] = [
|
||||
'id' => $model->id,
|
||||
'type' => $model->type,
|
||||
'message' => $model->message,
|
||||
'create_time' => $model->create_time,
|
||||
];
|
||||
}
|
||||
return $this->jsonSucc($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save() {
|
||||
$devices = $this->request->param('devices');
|
||||
$platform = trim($this->request->param('platform'));
|
||||
$type = trim($this->request->param('type'));
|
||||
$runType = trim($this->request->param('run_type'));
|
||||
$runTime = trim($this->request->param('run_time'));
|
||||
if (empty($devices)
|
||||
OR !is_array($devices)
|
||||
OR !isset(TaskModel::platformAssoc()[$platform])
|
||||
OR !isset(TaskModel::typeAssoc()[$type])
|
||||
OR !isset(TaskModel::runTypeAssoc()[$runType])) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
$timeTypes = [TaskModel::RUN_TYPE_TIMER, TaskModel::RUN_TYPE_DAILY];
|
||||
if (in_array($runType, $timeTypes)
|
||||
AND empty($runTime)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
$params = call_user_func_array(TaskModel::taskClasses()[$type] . '::params', [$this->request]);
|
||||
if (is_null($params)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
foreach ($devices as $deviceId) {
|
||||
$model = new TaskModel();
|
||||
$model->platform = $platform;
|
||||
$model->type = $type;
|
||||
$model->device_id = $deviceId;
|
||||
$model->params = $params;
|
||||
$model->run_type = $runType;
|
||||
$model->run_time = in_array($runType, $timeTypes) ? $runTime : '';
|
||||
$model->save();
|
||||
}
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete() {
|
||||
$id = intval($this->request->param('id'));
|
||||
if (empty($id)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
$model = TaskModel::get($id);
|
||||
if (empty($model)) {
|
||||
return $this->jsonFail('对象未找到');
|
||||
}
|
||||
|
||||
TaskDetailModel::where(1)
|
||||
->where('task_id', $model->id)
|
||||
->update([
|
||||
'is_deleted' => TaskModel::IS_DELETED_YES,
|
||||
]);
|
||||
|
||||
$model->is_deleted = TaskModel::IS_DELETED_YES;
|
||||
$model->save();
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function batchDelete() {
|
||||
$ids = $this->request->param('ids');
|
||||
if (empty($ids) OR !is_array($ids)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
TaskDetailModel::where(1)
|
||||
->whereIn('task_id', $ids)
|
||||
->update([
|
||||
'is_deleted' => TaskModel::IS_DELETED_YES,
|
||||
]);
|
||||
|
||||
TaskModel::where(1)
|
||||
->whereIn('id', $ids)
|
||||
->update([
|
||||
'is_deleted' => TaskModel::IS_DELETED_YES,
|
||||
]);
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取执行方式
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function runTypeAssoc() {
|
||||
return $this->jsonSucc($this->assocToList(TaskModel::runTypeAssoc()));
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
class UploadController extends BaseLoginController {
|
||||
|
||||
// 上传图片
|
||||
public function index() {
|
||||
if (!empty($_FILES)
|
||||
AND !empty($_FILES['file'])
|
||||
AND is_uploaded_file($_FILES['file']['tmp_name'])) {
|
||||
$ext = strtolower(trim(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)));
|
||||
$dir = ROOT_PATH . DS . 'public' . DS;
|
||||
$path = 'upload/' . $ext . '/' . date('Y-m-d') . '/' . time() . '-' . uniqid() . '.' . $ext;
|
||||
if (is_dir(dirname($dir . $path)) OR @mkdir(dirname($dir . $path), 0777, TRUE)) {
|
||||
if (move_uploaded_file($_FILES['file']['tmp_name'], $dir . $path)) {
|
||||
return json([
|
||||
'name' => $_FILES['file']['name'],
|
||||
'path' => $path,
|
||||
'url' => $this->absoluteUrl($path),
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($_FILES)) {
|
||||
return json([
|
||||
'url' => '',
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function editor() {
|
||||
if (!empty($_FILES)
|
||||
AND !empty($_FILES['upload'])
|
||||
AND is_uploaded_file($_FILES['upload']['tmp_name'])) {
|
||||
$ext = strtolower(trim(pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION)));
|
||||
$dir = ROOT_PATH . DS . 'public' . DS;
|
||||
$path = 'upload/' . $ext . '/' . date('Y-m-d') . '/' . time() . '-' . uniqid() . '.' . $ext;
|
||||
if (is_dir(dirname($dir . $path)) OR @mkdir(dirname($dir . $path), 0777, TRUE)) {
|
||||
if (move_uploaded_file($_FILES['upload']['tmp_name'], $dir . $path)) {
|
||||
return json([
|
||||
'uploaded' => true,
|
||||
'url' => $this->absoluteUrl($path),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json([
|
||||
'uploaded' => false,
|
||||
'url' => '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\UserModel;
|
||||
use app\common\model\UserTokenModel;
|
||||
|
||||
class UserController extends BaseController {
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function login() {
|
||||
$username = trim($this->request->param('username'));
|
||||
$password = trim($this->request->param('password'));
|
||||
if (empty($username)
|
||||
OR empty($password)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
$user = UserModel::get([
|
||||
'username' => $username,
|
||||
'password' => md5($password),
|
||||
]);
|
||||
if (empty($user)) {
|
||||
return $this->jsonFail('账号/密码错误');
|
||||
}
|
||||
if ($user->status != UserModel::STATUS_ACTIVE) {
|
||||
return $this->jsonFail('账号不可用');
|
||||
}
|
||||
|
||||
$tokenModel = new UserTokenModel();
|
||||
$tokenModel->token = md5(time() . uniqid());
|
||||
$tokenModel->user_id = $user->id;
|
||||
if ($tokenModel->save()) {
|
||||
$user->login_time = time();
|
||||
$user->login_count += 1;
|
||||
$user->login_ip = $this->request->ip();
|
||||
if ($user->save()) {
|
||||
return $this->jsonSucc([
|
||||
'logged' => TRUE,
|
||||
'token' => $tokenModel->token,
|
||||
'token_expired' => 365 * 24 * 3600,
|
||||
'user' => $this->userJson($user),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->jsonFail('登录失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function get() {
|
||||
if (!empty($this->userModel)) {
|
||||
return $this->jsonSucc([
|
||||
'logged' => TRUE,
|
||||
'user' => $this->userJson($this->userModel),
|
||||
]);
|
||||
} else {
|
||||
return $this->jsonSucc([
|
||||
'logged' => FALSE,
|
||||
'user' => NULL,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置密码
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function password() {
|
||||
if (empty($this->userModel)) {
|
||||
return $this->jsonFail('未登录');
|
||||
}
|
||||
|
||||
$oldPassword = trim($this->request->param('oldPassword'));
|
||||
$newPassword = trim($this->request->param('newPassword'));
|
||||
if (empty($oldPassword)
|
||||
OR empty($newPassword)
|
||||
OR strlen($newPassword) < 6
|
||||
OR strlen($newPassword) > 16) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
if (md5($oldPassword) != $this->userModel->password) {
|
||||
return $this->jsonFail('原密码输入错误');
|
||||
}
|
||||
|
||||
$this->userModel->password = md5($newPassword);
|
||||
$this->userModel->save();
|
||||
|
||||
return $this->jsonSucc([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function logout() {
|
||||
if (!empty($this->tokenModel)) {
|
||||
$this->tokenModel->delete();
|
||||
}
|
||||
|
||||
return $this->jsonSucc([]);
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\XianyuModel;
|
||||
use think\db\Query;
|
||||
|
||||
class XianyuController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 闲鱼列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = XianyuModel::where(1);
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('username', '%' . $keywords . '%', 'OR');
|
||||
$q->whereLike('nickname', '%' . $keywords . '%', 'OR');
|
||||
$q->whereLike('remark', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function remark() {
|
||||
$id = intval($this->request->param('id'));
|
||||
$remark = trim($this->request->param('remark'));
|
||||
if (empty($id)) {
|
||||
return $this->jsonFail('参数错误');
|
||||
}
|
||||
|
||||
$model = XianyuModel::get($id);
|
||||
if (empty($model)) {
|
||||
return $this->jsonFail('对象未找到');
|
||||
}
|
||||
|
||||
$model->remark = $remark;
|
||||
$model->save();
|
||||
|
||||
return $this->jsonSucc();
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\DeviceModel;
|
||||
use app\common\model\XianyuProductModel;
|
||||
use app\common\model\XianyuModel;
|
||||
use think\db\Query;
|
||||
|
||||
class XianyuProductController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$username = trim($this->request->param('username'));
|
||||
$onSale = trim($this->request->param('on_sale'));
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = XianyuProductModel::where(1);
|
||||
if (!empty($username)) {
|
||||
$query->where('username', $username);
|
||||
}
|
||||
if (isset(XianyuProductModel::onSaleAssoc()[$onSale])) {
|
||||
$query->where('on_sale', $onSale);
|
||||
}
|
||||
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('title', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$device = DeviceModel::get($model->device_id);
|
||||
$xianyu = XianyuModel::get(['username' => $model->username]);
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'device_number' => $device ? $device->number : '',
|
||||
'on_sale_name' => XianyuProductModel::onSaleAssoc()[$model->on_sale],
|
||||
'nickname' => $xianyu ? $xianyu->nickname : '',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
'xianyus' => $this->assocToList(XianyuModel::assoc()),
|
||||
'onSales' => $this->assocToList(XianyuProductModel::onSaleAssoc()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\DeviceModel;
|
||||
use app\common\model\XianyuModel;
|
||||
use app\common\model\XianyuShopModel;
|
||||
use think\db\Query;
|
||||
|
||||
class XianyuShopController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$username = trim($this->request->param('username'));
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = XianyuShopModel::where(1);
|
||||
if (!empty($username)) {
|
||||
$query->where('username', $username);
|
||||
}
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
//$q->whereLike('title', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$device = DeviceModel::get($model->device_id);
|
||||
$xianyu = XianyuModel::get(['username' => $model->username]);
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'device_number' => $device ? $device->number : '',
|
||||
'nickname' => $xianyu ? $xianyu->nickname : '',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
'xianyus' => $this->assocToList(XianyuModel::assoc()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\controller;
|
||||
|
||||
use app\common\model\DeviceModel;
|
||||
use app\common\model\XianyuModel;
|
||||
use app\common\model\XianyuShopProductModel;
|
||||
use think\db\Query;
|
||||
|
||||
class XianyuShopProductController extends BaseLoginController {
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index() {
|
||||
$username = trim($this->request->param('username'));
|
||||
$pay = trim($this->request->param('pay'));
|
||||
$keywords = trim($this->request->param('keywords'));
|
||||
$pageNo = intval($this->request->param('page'));
|
||||
$pageSize = intval($this->request->param('pageSize'));
|
||||
if ($pageNo <= 0) {
|
||||
$pageNo = 1;
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = 30;
|
||||
}
|
||||
|
||||
$query = XianyuShopProductModel::where(1);
|
||||
if (!empty($username)) {
|
||||
$query->where('username', $username);
|
||||
}
|
||||
if (isset(XianyuShopProductModel::payAssoc()[$pay])) {
|
||||
$query->where('pay', $pay);
|
||||
}
|
||||
|
||||
if (!empty($keywords)) {
|
||||
$query->where(function (Query $q) use ($keywords) {
|
||||
$q->whereLike('title', '%' . $keywords . '%', 'OR');
|
||||
});
|
||||
}
|
||||
|
||||
$totalCount = $query->count();
|
||||
$pageCount = $totalCount > 0 ? ceil($totalCount / $pageSize) : 1;
|
||||
if ($pageNo > $pageCount) {
|
||||
$pageNo = $pageCount;
|
||||
}
|
||||
|
||||
$query->order('id', 'DESC');
|
||||
$query->limit(($pageNo - 1) * $pageSize, $pageSize);
|
||||
|
||||
$list = [];
|
||||
foreach ($query->select() as $model) {
|
||||
$device = DeviceModel::get($model->device_id);
|
||||
$xianyu = XianyuModel::get(['username' => $model->username]);
|
||||
$list[] = array_merge($model->toArray(), [
|
||||
'device_number' => $device ? $device->number : '',
|
||||
'pay_name' => XianyuShopProductModel::payAssoc()[$model->pay],
|
||||
'nickname' => $xianyu ? $xianyu->nickname : '',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->jsonSucc([
|
||||
'list' => $list,
|
||||
'page' => $pageNo,
|
||||
'pageCount' => $pageCount,
|
||||
'totalCount' => $totalCount,
|
||||
'xianyus' => $this->assocToList(XianyuModel::assoc()),
|
||||
'pays' => $this->assocToList(XianyuShopProductModel::payAssoc()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\backend\model;
|
||||
|
||||
class TimeRangeModel {
|
||||
|
||||
/**
|
||||
* 获取年
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function getYears() {
|
||||
$years = [];
|
||||
$timeS = strtotime(date('Y') . '-01-01');
|
||||
$timeE = strtotime((date('Y', $timeS) + 1) . '-01-01') - 1;
|
||||
for ($i = 0; $i > -3; $i --) {
|
||||
$years[] = [
|
||||
'key' => $i,
|
||||
'label' => date('Y 年', $timeS),
|
||||
'timeS' => $timeS,
|
||||
'timeE' => $timeE,
|
||||
];
|
||||
|
||||
$timeE = $timeS - 1;
|
||||
$timeS = strtotime(date('Y', $timeE) . '-01-01');
|
||||
}
|
||||
|
||||
return $years;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月份
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function getMonths() {
|
||||
$months = [];
|
||||
$timeS = strtotime(date('Y-m') . '-01');
|
||||
$timeE = $timeS + date('t', $timeS) * 24 * 3600 - 1;
|
||||
for ($i = 0; $i > -24; $i --) {
|
||||
$months[] = [
|
||||
'key' => $i,
|
||||
'label' => date('Y 年 m 月', $timeS) . ' (' . date('Y.m.d', $timeS) . '-' . date('Y.m.d', $timeE) . ')',
|
||||
'timeS' => $timeS,
|
||||
'timeE' => $timeE,
|
||||
];
|
||||
|
||||
$timeS = strtotime(date('Y-m', $timeS - 1) . '-01');
|
||||
$timeE = $timeS + date('t', $timeS) * 24 * 3600 - 1;
|
||||
}
|
||||
|
||||
return $months;
|
||||
}
|
||||
}
|
||||
50
Server/application/command/InitDatabase.php
Normal file
50
Server/application/command/InitDatabase.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\Db;
|
||||
use think\facade\Config;
|
||||
|
||||
class InitDatabase extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('init:database')
|
||||
->setDescription('初始化数据库,创建必要的表结构');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$output->writeln('开始初始化数据库...');
|
||||
|
||||
try {
|
||||
// 读取SQL文件
|
||||
$sqlFile = app()->getAppPath() . 'common/database/tk_users.sql';
|
||||
|
||||
if (!file_exists($sqlFile)) {
|
||||
$output->error('SQL文件不存在: ' . $sqlFile);
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = file_get_contents($sqlFile);
|
||||
|
||||
// 分割SQL语句
|
||||
$sqlArr = explode(';', $sql);
|
||||
|
||||
// 执行SQL语句
|
||||
foreach ($sqlArr as $statement) {
|
||||
$statement = trim($statement);
|
||||
if ($statement) {
|
||||
Db::execute($statement);
|
||||
$output->writeln('执行SQL: ' . mb_substr($statement, 0, 100) . '...');
|
||||
}
|
||||
}
|
||||
|
||||
$output->info('数据库初始化完成!');
|
||||
} catch (\Exception $e) {
|
||||
$output->error('数据库初始化失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Server/application/common/config/route.php
Normal file
37
Server/application/common/config/route.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
// common模块路由配置
|
||||
|
||||
use think\facade\Route;
|
||||
|
||||
// 添加测试路由
|
||||
Route::get('api/test', function() {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '路由测试成功',
|
||||
'data' => [
|
||||
'time' => date('Y-m-d H:i:s'),
|
||||
'module' => 'common'
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
// 数据库初始化路由
|
||||
Route::get('api/database/init', 'app\\common\\controller\\Database@init');
|
||||
Route::get('api/database/test', 'app\\common\\controller\\Database@test');
|
||||
Route::get('api/database/update-password', 'app\\common\\controller\\Database@updatePassword');
|
||||
Route::get('api/database/debug-password', 'app\\common\\controller\\Database@debugPassword');
|
||||
Route::get('api/database/reset-password', 'app\\common\\controller\\Database@resetPassword');
|
||||
|
||||
// 定义RESTful风格的API路由 - 认证相关
|
||||
Route::group('api/auth', function () {
|
||||
// 无需认证的接口
|
||||
Route::post('login', 'app\\common\\controller\\Auth@login'); // 账号密码登录
|
||||
Route::post('mobile-login', 'app\\common\\controller\\Auth@mobileLogin'); // 手机号验证码登录
|
||||
Route::post('code', 'app\\common\\controller\\Auth@sendCode'); // 发送验证码
|
||||
|
||||
// 需要JWT认证的接口
|
||||
Route::get('info', 'app\\common\\controller\\Auth@info')->middleware(['jwt']); // 获取用户信息
|
||||
Route::post('refresh', 'app\\common\\controller\\Auth@refresh')->middleware(['jwt']); // 刷新令牌
|
||||
});
|
||||
|
||||
return [];
|
||||
167
Server/application/common/controller/Auth.php
Normal file
167
Server/application/common/controller/Auth.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
namespace app\common\controller;
|
||||
|
||||
use app\common\helper\ResponseHelper;
|
||||
use app\common\service\AuthService;
|
||||
use think\Controller;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
* 处理用户登录和身份验证
|
||||
*/
|
||||
class Auth extends Controller
|
||||
{
|
||||
/**
|
||||
* 允许跨域请求的域名
|
||||
* @var string
|
||||
*/
|
||||
protected $allowOrigin = '*';
|
||||
|
||||
/**
|
||||
* 认证服务实例
|
||||
* @var AuthService
|
||||
*/
|
||||
protected $authService;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* 设置跨域相关响应头
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
// 允许跨域访问
|
||||
header('Access-Control-Allow-Origin: ' . $this->allowOrigin);
|
||||
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
|
||||
// 预检请求直接返回200
|
||||
if (Request::method(true) == 'OPTIONS') {
|
||||
exit();
|
||||
}
|
||||
|
||||
// 初始化认证服务
|
||||
$this->authService = new AuthService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
// 获取登录参数
|
||||
$params = Request::only(['username', 'password', 'is_encrypted']);
|
||||
|
||||
// 参数验证
|
||||
$validate = validate('common/Auth');
|
||||
if (!$validate->scene('login')->check($params)) {
|
||||
return ResponseHelper::error($validate->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
// 判断密码是否已加密
|
||||
$isEncrypted = isset($params['is_encrypted']) && $params['is_encrypted'] === true;
|
||||
|
||||
// 调用登录服务
|
||||
$result = $this->authService->login(
|
||||
$params['username'],
|
||||
$params['password'],
|
||||
Request::ip(),
|
||||
$isEncrypted
|
||||
);
|
||||
return ResponseHelper::success($result, '登录成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号验证码登录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function mobileLogin()
|
||||
{
|
||||
// 获取登录参数
|
||||
$params = Request::only(['mobile', 'code', 'is_encrypted']);
|
||||
|
||||
// 参数验证
|
||||
$validate = validate('common/Auth');
|
||||
if (!$validate->scene('mobile_login')->check($params)) {
|
||||
return ResponseHelper::error($validate->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
// 判断验证码是否已加密
|
||||
$isEncrypted = isset($params['is_encrypted']) && $params['is_encrypted'] === true;
|
||||
|
||||
// 调用手机号登录服务
|
||||
$result = $this->authService->mobileLogin(
|
||||
$params['mobile'],
|
||||
$params['code'],
|
||||
Request::ip(),
|
||||
$isEncrypted
|
||||
);
|
||||
return ResponseHelper::success($result, '登录成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function sendCode()
|
||||
{
|
||||
// 获取参数
|
||||
$params = Request::only(['mobile', 'type']);
|
||||
|
||||
// 参数验证
|
||||
$validate = validate('common/Auth');
|
||||
if (!$validate->scene('send_code')->check($params)) {
|
||||
return ResponseHelper::error($validate->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用发送验证码服务
|
||||
$result = $this->authService->sendLoginCode(
|
||||
$params['mobile'],
|
||||
$params['type']
|
||||
);
|
||||
return ResponseHelper::success($result, '验证码发送成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function info()
|
||||
{
|
||||
try {
|
||||
$result = $this->authService->getUserInfo(request()->userInfo);
|
||||
return ResponseHelper::success($result);
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::unauthorized($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
try {
|
||||
$result = $this->authService->refreshToken(request()->userInfo);
|
||||
return ResponseHelper::success($result, '刷新成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::unauthorized($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
196
Server/application/common/controller/Database.php
Normal file
196
Server/application/common/controller/Database.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
namespace app\common\controller;
|
||||
|
||||
use think\Controller;
|
||||
use think\Db;
|
||||
use think\facade\Config;
|
||||
use app\common\helper\ResponseHelper;
|
||||
|
||||
/**
|
||||
* 数据库控制器
|
||||
* 用于初始化数据库
|
||||
*/
|
||||
class Database extends Controller
|
||||
{
|
||||
/**
|
||||
* 初始化数据库
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
try {
|
||||
// 创建表结构
|
||||
$createTableSql = "
|
||||
CREATE TABLE IF NOT EXISTS `tk_users` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`username` varchar(50) NOT NULL COMMENT '用户名',
|
||||
`password` varchar(60) NOT NULL COMMENT '密码',
|
||||
`mobile` varchar(11) DEFAULT NULL COMMENT '登录手机号',
|
||||
`identity_id` int(10) DEFAULT NULL COMMENT '身份信息',
|
||||
`auth_id` int(10) DEFAULT NULL COMMENT '权限id',
|
||||
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`delete_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_username` (`username`),
|
||||
UNIQUE KEY `idx_mobile` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
";
|
||||
|
||||
Db::execute($createTableSql);
|
||||
|
||||
// 检查是否已存在admin用户
|
||||
$adminExists = Db::table('tk_users')->where('username', 'admin')->find();
|
||||
|
||||
if (!$adminExists) {
|
||||
// 生成密码的加密值
|
||||
$hashedPassword = password_hash('123456', PASSWORD_BCRYPT);
|
||||
|
||||
// 插入测试数据
|
||||
$insertDataSql = "
|
||||
INSERT INTO `tk_users` (`username`, `password`, `mobile`, `identity_id`, `auth_id`) VALUES
|
||||
('admin', '{$hashedPassword}', '13800138000', 1, 1);
|
||||
";
|
||||
|
||||
Db::execute($insertDataSql);
|
||||
}
|
||||
|
||||
return ResponseHelper::success(null, '数据库初始化完成');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('数据库初始化失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试数据库连接和查询
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
try {
|
||||
// 查询用户表中的数据
|
||||
$users = Db::table('tk_users')->select();
|
||||
|
||||
return ResponseHelper::success([
|
||||
'count' => count($users),
|
||||
'users' => $users
|
||||
], '数据库查询成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('数据库查询失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户密码
|
||||
* @param string $username 用户名
|
||||
* @param string $password 新密码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function updatePassword($username = 'admin', $password = '123456')
|
||||
{
|
||||
try {
|
||||
// 生成密码的加密值
|
||||
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
|
||||
|
||||
// 更新数据库中的用户密码
|
||||
$result = Db::table('tk_users')
|
||||
->where('username', $username)
|
||||
->update(['password' => $hashedPassword]);
|
||||
|
||||
if ($result) {
|
||||
return ResponseHelper::success([
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'hashed_password' => $hashedPassword
|
||||
], '密码更新成功');
|
||||
} else {
|
||||
return ResponseHelper::error('用户不存在或密码未更改');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('密码更新失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试密码验证
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function debugPassword($username = 'admin', $password = '123456')
|
||||
{
|
||||
try {
|
||||
// 查询用户
|
||||
$user = Db::table('tk_users')->where('username', $username)->find();
|
||||
|
||||
if (!$user) {
|
||||
return ResponseHelper::error('用户不存在');
|
||||
}
|
||||
|
||||
// 生成新的密码哈希
|
||||
$newHash = password_hash($password, PASSWORD_BCRYPT);
|
||||
|
||||
// 验证密码
|
||||
$isValid = password_verify($password, $user['password']);
|
||||
|
||||
// 更新密码(确保使用正确的哈希算法)
|
||||
if (!$isValid) {
|
||||
Db::table('tk_users')
|
||||
->where('username', $username)
|
||||
->update(['password' => $newHash]);
|
||||
}
|
||||
|
||||
return ResponseHelper::success([
|
||||
'username' => $username,
|
||||
'stored_hash' => $user['password'],
|
||||
'new_hash' => $newHash,
|
||||
'is_valid' => $isValid,
|
||||
'password_info' => password_get_info($user['password'])
|
||||
], '密码验证调试信息');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('密码验证调试失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
* @param string $username 用户名
|
||||
* @param string $password 新密码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function resetPassword($username = 'admin', $password = '123456')
|
||||
{
|
||||
try {
|
||||
// 查询用户
|
||||
$user = Db::table('tk_users')->where('username', $username)->find();
|
||||
|
||||
if (!$user) {
|
||||
return ResponseHelper::error('用户不存在');
|
||||
}
|
||||
|
||||
// 使用正确的哈希算法生成密码
|
||||
$hashedPassword = password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]);
|
||||
|
||||
// 更新数据库中的用户密码
|
||||
$result = Db::table('tk_users')
|
||||
->where('username', $username)
|
||||
->update(['password' => $hashedPassword]);
|
||||
|
||||
if ($result) {
|
||||
// 验证密码是否正确
|
||||
$isValid = password_verify($password, $hashedPassword);
|
||||
|
||||
return ResponseHelper::success([
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'hashed_password' => $hashedPassword,
|
||||
'is_valid' => $isValid
|
||||
], '密码重置成功');
|
||||
} else {
|
||||
return ResponseHelper::error('密码重置失败');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('密码重置失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
use think\migration\db\Column;
|
||||
|
||||
class CreateUsersTable extends Migrator
|
||||
{
|
||||
/**
|
||||
* 创建用户表
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$table = $this->table('users', [
|
||||
'engine' => 'InnoDB',
|
||||
'comment' => '用户表',
|
||||
'id' => 'id',
|
||||
'signed' => false,
|
||||
]);
|
||||
|
||||
$table->addColumn('username', 'string', [
|
||||
'limit' => 50,
|
||||
'null' => false,
|
||||
'comment' => '用户名',
|
||||
])
|
||||
->addColumn('password', 'string', [
|
||||
'limit' => 60,
|
||||
'null' => false,
|
||||
'comment' => '密码',
|
||||
])
|
||||
->addColumn('mobile', 'string', [
|
||||
'limit' => 11,
|
||||
'null' => true,
|
||||
'comment' => '登录手机号',
|
||||
])
|
||||
->addColumn('identity_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'null' => true,
|
||||
'comment' => '身份信息',
|
||||
])
|
||||
->addColumn('auth_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'null' => true,
|
||||
'comment' => '权限id',
|
||||
])
|
||||
->addColumn('create_at', 'timestamp', [
|
||||
'null' => false,
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
'comment' => '创建时间',
|
||||
])
|
||||
->addColumn('update_at', 'timestamp', [
|
||||
'null' => false,
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
'update' => 'CURRENT_TIMESTAMP',
|
||||
'comment' => '修改时间',
|
||||
])
|
||||
->addColumn('delete_at', 'timestamp', [
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'comment' => '删除时间',
|
||||
])
|
||||
->addIndex(['username'], [
|
||||
'unique' => true,
|
||||
'name' => 'idx_username',
|
||||
])
|
||||
->addIndex(['mobile'], [
|
||||
'unique' => true,
|
||||
'name' => 'idx_mobile',
|
||||
])
|
||||
->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户表
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$this->dropTable('users');
|
||||
}
|
||||
}
|
||||
19
Server/application/common/database/tk_users.sql
Normal file
19
Server/application/common/database/tk_users.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- 创建用户表
|
||||
CREATE TABLE IF NOT EXISTS `tk_users` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`username` varchar(50) NOT NULL COMMENT '用户名',
|
||||
`password` varchar(60) NOT NULL COMMENT '密码',
|
||||
`mobile` varchar(11) DEFAULT NULL COMMENT '登录手机号',
|
||||
`identity_id` int(10) DEFAULT NULL COMMENT '身份信息',
|
||||
`auth_id` int(10) DEFAULT NULL COMMENT '权限id',
|
||||
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`delete_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_username` (`username`),
|
||||
UNIQUE KEY `idx_mobile` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
|
||||
-- 插入测试数据
|
||||
INSERT INTO `tk_users` (`username`, `password`, `mobile`, `identity_id`, `auth_id`) VALUES
|
||||
('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '13800138000', 1, 1); -- 密码为:password
|
||||
57
Server/application/common/helper/ResponseHelper.php
Normal file
57
Server/application/common/helper/ResponseHelper.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace app\common\helper;
|
||||
|
||||
class ResponseHelper
|
||||
{
|
||||
/**
|
||||
* 成功响应
|
||||
* @param mixed $data 响应数据
|
||||
* @param string $msg 响应消息
|
||||
* @param int $code 响应代码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function success($data = null, $msg = '操作成功', $code = 200)
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误响应
|
||||
* @param string $msg 错误消息
|
||||
* @param int $code 错误代码
|
||||
* @param mixed $data 错误数据
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function error($msg = '操作失败', $code = 400, $data = null)
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未授权响应
|
||||
* @param string $msg 错误消息
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function unauthorized($msg = '未授权访问')
|
||||
{
|
||||
return self::error($msg, 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止访问响应
|
||||
* @param string $msg 错误消息
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function forbidden($msg = '禁止访问')
|
||||
{
|
||||
return self::error($msg, 403);
|
||||
}
|
||||
}
|
||||
163
Server/application/common/model/User.php
Normal file
163
Server/application/common/model/User.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
use SoftDelete;
|
||||
|
||||
/**
|
||||
* 数据表名
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'tk_users';
|
||||
|
||||
/**
|
||||
* 主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* 自动写入时间戳
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 创建时间字段
|
||||
* @var string
|
||||
*/
|
||||
protected $createTime = 'create_at';
|
||||
|
||||
/**
|
||||
* 更新时间字段
|
||||
* @var string
|
||||
*/
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
/**
|
||||
* 软删除字段
|
||||
* @var string
|
||||
*/
|
||||
protected $deleteTime = 'delete_at';
|
||||
|
||||
/**
|
||||
* 隐藏属性
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['password', 'delete_at'];
|
||||
|
||||
/**
|
||||
* 获取管理员用户信息
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码(可能是加密后的)
|
||||
* @param bool $isEncrypted 密码是否已加密
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getAdminUser($username, $password, $isEncrypted = false)
|
||||
{
|
||||
// 查询用户
|
||||
$user = self::where('username', $username)->find();
|
||||
|
||||
if (!$user) {
|
||||
// 记录日志
|
||||
\think\facade\Log::info('用户不存在', ['username' => $username]);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 记录密码验证信息
|
||||
\think\facade\Log::info('密码验证', [
|
||||
'username' => $username,
|
||||
'input_password' => $password,
|
||||
'stored_hash' => $user->password,
|
||||
'is_encrypted' => $isEncrypted,
|
||||
'password_info' => password_get_info($user->password)
|
||||
]);
|
||||
|
||||
// 验证密码
|
||||
$isValid = false;
|
||||
|
||||
if ($isEncrypted) {
|
||||
// 前端已加密,直接比较哈希值
|
||||
// 注意:这里需要确保前端和后端使用相同的加密算法和盐值
|
||||
$storedHash = self::getStoredHash($user->password);
|
||||
$isValid = hash_equals($storedHash, $password);
|
||||
|
||||
\think\facade\Log::info('加密密码验证', [
|
||||
'username' => $username,
|
||||
'stored_hash' => $storedHash,
|
||||
'input_hash' => $password,
|
||||
'is_valid' => $isValid
|
||||
]);
|
||||
} else {
|
||||
// 未加密,使用password_verify验证
|
||||
$isValid = password_verify($password, $user->password);
|
||||
}
|
||||
|
||||
\think\facade\Log::info('密码验证结果', [
|
||||
'username' => $username,
|
||||
'is_valid' => $isValid,
|
||||
'is_encrypted' => $isEncrypted
|
||||
]);
|
||||
|
||||
if (!$isValid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'name' => $user->username, // 暂时使用username作为name
|
||||
'role' => 'admin', // 暂时固定为admin角色
|
||||
'permissions' => ['*'], // 暂时拥有所有权限
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储的哈希值
|
||||
* 用于前端加密密码的验证
|
||||
* @param string $bcryptHash 数据库中存储的bcrypt哈希值
|
||||
* @return string 用于前端验证的哈希值
|
||||
*/
|
||||
protected static function getStoredHash($bcryptHash)
|
||||
{
|
||||
// 这里需要实现与前端相同的加密算法
|
||||
// 例如,如果前端使用SHA256加盐,这里需要提取原始密码并进行相同的处理
|
||||
// 注意:这只是一个示例,实际实现可能需要根据您的具体需求调整
|
||||
|
||||
// 假设我们能够从bcrypt哈希中提取原始密码(实际上这是不可能的,这里只是示例)
|
||||
// 在实际应用中,您需要在用户注册或修改密码时同时存储前端加密的哈希值
|
||||
$originalPassword = '123456'; // 这里应该是从数据库中获取的原始密码
|
||||
$salt = 'yishi_salt_2024'; // 与前端相同的盐值
|
||||
|
||||
// 使用与前端相同的算法
|
||||
return hash('sha256', $originalPassword . $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过手机号获取用户信息
|
||||
* @param string $mobile 手机号
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getUserByMobile($mobile)
|
||||
{
|
||||
// 查询用户
|
||||
$user = self::where('mobile', $mobile)->find();
|
||||
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'name' => $user->username, // 暂时使用username作为name
|
||||
'mobile' => $user->mobile,
|
||||
'role' => 'user', // 暂时固定为user角色
|
||||
'permissions' => ['user'], // 暂时拥有用户权限
|
||||
];
|
||||
}
|
||||
}
|
||||
157
Server/application/common/service/AuthService.php
Normal file
157
Server/application/common/service/AuthService.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\model\User;
|
||||
use app\common\util\JwtUtil;
|
||||
use think\facade\Log;
|
||||
|
||||
class AuthService
|
||||
{
|
||||
/**
|
||||
* 令牌有效期(秒)
|
||||
*/
|
||||
const TOKEN_EXPIRE = 7200;
|
||||
|
||||
/**
|
||||
* 短信服务实例
|
||||
* @var SmsService
|
||||
*/
|
||||
protected $smsService;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->smsService = new SmsService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码(可能是加密后的)
|
||||
* @param string $ip 登录IP
|
||||
* @param bool $isEncrypted 密码是否已加密
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function login($username, $password, $ip, $isEncrypted = false)
|
||||
{
|
||||
// 获取用户信息
|
||||
$user = User::getAdminUser($username, $password, $isEncrypted);
|
||||
|
||||
if (empty($user)) {
|
||||
// 记录登录失败
|
||||
Log::info('登录失败', ['username' => $username, 'ip' => $ip, 'is_encrypted' => $isEncrypted]);
|
||||
throw new \Exception('用户名或密码错误');
|
||||
}
|
||||
|
||||
// 生成JWT令牌
|
||||
$token = JwtUtil::createToken($user, self::TOKEN_EXPIRE);
|
||||
$expireTime = time() + self::TOKEN_EXPIRE;
|
||||
|
||||
// 记录登录成功
|
||||
Log::info('登录成功', ['username' => $username, 'ip' => $ip]);
|
||||
|
||||
return [
|
||||
'token' => $token,
|
||||
'token_expired' => $expireTime,
|
||||
'member' => $user
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号验证码登录
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码(可能是加密后的)
|
||||
* @param string $ip 登录IP
|
||||
* @param bool $isEncrypted 验证码是否已加密
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function mobileLogin($mobile, $code, $ip, $isEncrypted = false)
|
||||
{
|
||||
// 验证验证码
|
||||
if (!$this->smsService->verifyCode($mobile, $code, 'login', $isEncrypted)) {
|
||||
Log::info('验证码验证失败', ['mobile' => $mobile, 'ip' => $ip, 'is_encrypted' => $isEncrypted]);
|
||||
throw new \Exception('验证码错误或已过期');
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
$user = User::getUserByMobile($mobile);
|
||||
if (empty($user)) {
|
||||
Log::info('用户不存在', ['mobile' => $mobile, 'ip' => $ip]);
|
||||
throw new \Exception('用户不存在');
|
||||
}
|
||||
|
||||
// 生成JWT令牌
|
||||
$token = JwtUtil::createToken($user, self::TOKEN_EXPIRE);
|
||||
$expireTime = time() + self::TOKEN_EXPIRE;
|
||||
|
||||
// 记录登录成功
|
||||
Log::info('手机号登录成功', ['mobile' => $mobile, 'ip' => $ip]);
|
||||
|
||||
return [
|
||||
'token' => $token,
|
||||
'token_expired' => $expireTime,
|
||||
'member' => $user
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送登录验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function sendLoginCode($mobile, $type)
|
||||
{
|
||||
return $this->smsService->sendCode($mobile, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param array $userInfo JWT中的用户信息
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getUserInfo($userInfo)
|
||||
{
|
||||
if (empty($userInfo)) {
|
||||
throw new \Exception('获取用户信息失败');
|
||||
}
|
||||
|
||||
// 移除不需要返回的字段
|
||||
unset($userInfo['exp']);
|
||||
unset($userInfo['iat']);
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* @param array $userInfo JWT中的用户信息
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function refreshToken($userInfo)
|
||||
{
|
||||
if (empty($userInfo)) {
|
||||
throw new \Exception('刷新令牌失败');
|
||||
}
|
||||
|
||||
// 移除过期时间信息
|
||||
unset($userInfo['exp']);
|
||||
unset($userInfo['iat']);
|
||||
|
||||
// 生成新令牌
|
||||
$token = JwtUtil::createToken($userInfo, self::TOKEN_EXPIRE);
|
||||
$expireTime = time() + self::TOKEN_EXPIRE;
|
||||
|
||||
return [
|
||||
'token' => $token,
|
||||
'token_expired' => $expireTime
|
||||
];
|
||||
}
|
||||
}
|
||||
202
Server/application/common/service/SmsService.php
Normal file
202
Server/application/common/service/SmsService.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
namespace app\common\service;
|
||||
|
||||
use think\facade\Cache;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 短信服务类
|
||||
*/
|
||||
class SmsService
|
||||
{
|
||||
/**
|
||||
* 验证码有效期(秒)
|
||||
*/
|
||||
const CODE_EXPIRE = 300;
|
||||
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
const CODE_LENGTH = 4;
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型 (login, register)
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function sendCode($mobile, $type)
|
||||
{
|
||||
// 检查发送频率限制
|
||||
$this->checkSendLimit($mobile, $type);
|
||||
|
||||
// 生成验证码
|
||||
$code = $this->generateCode();
|
||||
|
||||
// 缓存验证码
|
||||
$this->saveCode($mobile, $code, $type);
|
||||
|
||||
// 发送验证码(实际项目中对接短信平台)
|
||||
$this->doSend($mobile, $code, $type);
|
||||
|
||||
// 记录日志
|
||||
Log::info('发送验证码', [
|
||||
'mobile' => $mobile,
|
||||
'type' => $type,
|
||||
'code' => $code
|
||||
]);
|
||||
|
||||
return [
|
||||
'mobile' => $mobile,
|
||||
'expire' => self::CODE_EXPIRE,
|
||||
// 测试环境返回验证码,生产环境不应返回
|
||||
'code' => $code
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码(可能是加密后的)
|
||||
* @param string $type 验证码类型
|
||||
* @param bool $isEncrypted 验证码是否已加密
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyCode($mobile, $code, $type, $isEncrypted = false)
|
||||
{
|
||||
$cacheKey = $this->getCodeCacheKey($mobile, $type);
|
||||
$cacheCode = Cache::get($cacheKey);
|
||||
|
||||
if (!$cacheCode) {
|
||||
Log::info('验证码不存在或已过期', [
|
||||
'mobile' => $mobile,
|
||||
'type' => $type
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证码是否匹配
|
||||
$isValid = false;
|
||||
|
||||
if ($isEncrypted) {
|
||||
// 前端已加密,需要对缓存中的验证码进行相同的加密处理
|
||||
$encryptedCacheCode = $this->encryptCode($cacheCode);
|
||||
$isValid = hash_equals($encryptedCacheCode, $code);
|
||||
|
||||
// 记录日志
|
||||
Log::info('加密验证码验证', [
|
||||
'mobile' => $mobile,
|
||||
'cache_code' => $cacheCode,
|
||||
'encrypted_cache_code' => $encryptedCacheCode,
|
||||
'input_code' => $code,
|
||||
'is_valid' => $isValid
|
||||
]);
|
||||
} else {
|
||||
// 未加密,直接比较
|
||||
$isValid = ($cacheCode === $code);
|
||||
|
||||
// 记录日志
|
||||
Log::info('明文验证码验证', [
|
||||
'mobile' => $mobile,
|
||||
'cache_code' => $cacheCode,
|
||||
'input_code' => $code,
|
||||
'is_valid' => $isValid
|
||||
]);
|
||||
}
|
||||
|
||||
// 验证成功后删除缓存
|
||||
if ($isValid) {
|
||||
Cache::rm($cacheKey);
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查发送频率限制
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function checkSendLimit($mobile, $type)
|
||||
{
|
||||
$cacheKey = $this->getCodeCacheKey($mobile, $type);
|
||||
|
||||
// 检查是否存在未过期的验证码
|
||||
if (Cache::has($cacheKey)) {
|
||||
throw new \Exception('验证码已发送,请稍后再试');
|
||||
}
|
||||
|
||||
// 检查当日发送次数限制
|
||||
$limitKey = "sms_limit:{$mobile}:" . date('Ymd');
|
||||
$sendCount = Cache::get($limitKey, 0);
|
||||
|
||||
if ($sendCount >= 10) {
|
||||
throw new \Exception('今日发送次数已达上限');
|
||||
}
|
||||
|
||||
// 更新发送次数
|
||||
Cache::set($limitKey, $sendCount + 1, 86400);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机验证码
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCode()
|
||||
{
|
||||
// 生成4位数字验证码
|
||||
return sprintf("%0" . self::CODE_LENGTH . "d", mt_rand(0, pow(10, self::CODE_LENGTH) - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存验证码到缓存
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码
|
||||
* @param string $type 验证码类型
|
||||
*/
|
||||
protected function saveCode($mobile, $code, $type)
|
||||
{
|
||||
$cacheKey = $this->getCodeCacheKey($mobile, $type);
|
||||
Cache::set($cacheKey, $code, self::CODE_EXPIRE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发送验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码
|
||||
* @param string $type 验证码类型
|
||||
* @return bool
|
||||
*/
|
||||
protected function doSend($mobile, $code, $type)
|
||||
{
|
||||
// 实际项目中对接短信平台API
|
||||
// 这里仅做模拟,返回成功
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码缓存键名
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型
|
||||
* @return string
|
||||
*/
|
||||
protected function getCodeCacheKey($mobile, $type)
|
||||
{
|
||||
return "sms_code:{$mobile}:{$type}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密验证码
|
||||
* 使用与前端相同的加密算法
|
||||
* @param string $code 原始验证码
|
||||
* @return string 加密后的验证码
|
||||
*/
|
||||
protected function encryptCode($code)
|
||||
{
|
||||
// 使用与前端相同的加密算法
|
||||
$salt = 'yishi_salt_2024'; // 与前端相同的盐值
|
||||
return hash('sha256', $code . $salt);
|
||||
}
|
||||
}
|
||||
135
Server/application/common/util/JwtUtil.php
Normal file
135
Server/application/common/util/JwtUtil.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
namespace app\common\util;
|
||||
|
||||
use think\facade\Config;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* JWT工具类
|
||||
* 用于生成和验证JWT令牌
|
||||
*/
|
||||
class JwtUtil
|
||||
{
|
||||
/**
|
||||
* 密钥
|
||||
* @var string
|
||||
*/
|
||||
protected static $secret = 'YiShi@2023#JWT';
|
||||
|
||||
/**
|
||||
* 头部
|
||||
* @var array
|
||||
*/
|
||||
protected static $header = [
|
||||
'alg' => 'HS256', // 加密算法
|
||||
'typ' => 'JWT' // 类型
|
||||
];
|
||||
|
||||
/**
|
||||
* 创建JWT令牌
|
||||
* @param array $payload 载荷信息
|
||||
* @param int $expire 过期时间(秒),默认2小时
|
||||
* @return string
|
||||
*/
|
||||
public static function createToken($payload, $expire = 7200)
|
||||
{
|
||||
$header = self::base64UrlEncode(json_encode(self::$header, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 附加过期时间
|
||||
$payload['exp'] = time() + $expire;
|
||||
$payload['iat'] = time(); // 签发时间
|
||||
|
||||
$payload = self::base64UrlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||
$signature = self::signature($header . '.' . $payload, self::$secret);
|
||||
|
||||
return $header . '.' . $payload . '.' . $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌
|
||||
* @param string $token 令牌
|
||||
* @return array|bool 验证通过返回载荷信息,失败返回false
|
||||
*/
|
||||
public static function verifyToken($token)
|
||||
{
|
||||
if (empty($token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tokenArray = explode('.', $token);
|
||||
if (count($tokenArray) != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($header, $payload, $signature) = $tokenArray;
|
||||
|
||||
// 验证签名
|
||||
if (self::signature($header . '.' . $payload, self::$secret) !== $signature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解码载荷
|
||||
$payload = json_decode(self::base64UrlDecode($payload), true);
|
||||
|
||||
// 验证是否过期
|
||||
if (isset($payload['exp']) && $payload['exp'] < time()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* @param string $input 输入
|
||||
* @param string $key 密钥
|
||||
* @return string
|
||||
*/
|
||||
private static function signature($input, $key)
|
||||
{
|
||||
return self::base64UrlEncode(hash_hmac('sha256', $input, $key, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL安全的Base64编码
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private static function base64UrlEncode($input)
|
||||
{
|
||||
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL安全的Base64解码
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private static function base64UrlDecode($input)
|
||||
{
|
||||
$remainder = strlen($input) % 4;
|
||||
if ($remainder) {
|
||||
$input .= str_repeat('=', 4 - $remainder);
|
||||
}
|
||||
return base64_decode(str_replace(['-', '_'], ['+', '/'], $input));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中获取Token
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getRequestToken()
|
||||
{
|
||||
$authorization = Request::header('Authorization');
|
||||
if (!$authorization) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查Bearer前缀
|
||||
if (strpos($authorization, 'Bearer ') !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return substr($authorization, 7);
|
||||
}
|
||||
}
|
||||
52
Server/application/common/validate/Auth.php
Normal file
52
Server/application/common/validate/Auth.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace app\common\validate;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 认证相关验证器
|
||||
*/
|
||||
class Auth extends Validate
|
||||
{
|
||||
/**
|
||||
* 验证规则
|
||||
* @var array
|
||||
*/
|
||||
protected $rule = [
|
||||
'username' => 'require|length:3,20',
|
||||
'password' => 'require|length:6,64',
|
||||
'mobile' => 'require|mobile',
|
||||
'code' => 'require|length:4,6',
|
||||
'is_encrypted' => 'boolean',
|
||||
'type' => 'require|in:login,register',
|
||||
];
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
* @var array
|
||||
*/
|
||||
protected $message = [
|
||||
'username.require' => '用户名不能为空',
|
||||
'username.length' => '用户名长度必须在3-20个字符之间',
|
||||
'password.require' => '密码不能为空',
|
||||
'password.length' => '密码长度必须在6-64个字符之间',
|
||||
'mobile.require' => '手机号不能为空',
|
||||
'mobile.mobile' => '手机号格式不正确',
|
||||
'code.require' => '验证码不能为空',
|
||||
'code.length' => '验证码长度必须在4-6个字符之间',
|
||||
'is_encrypted.boolean' => '加密标志必须为布尔值',
|
||||
'type.require' => '验证码类型不能为空',
|
||||
'type.in' => '验证码类型不正确',
|
||||
];
|
||||
|
||||
/**
|
||||
* 验证场景
|
||||
* @var array
|
||||
*/
|
||||
protected $scene = [
|
||||
'login' => ['username', 'password', 'is_encrypted'],
|
||||
'mobile_login' => ['mobile', 'code', 'is_encrypted'],
|
||||
'refresh' => [],
|
||||
'send_code' => ['mobile', 'type'],
|
||||
];
|
||||
}
|
||||
16
Server/application/devices/config/route.php
Normal file
16
Server/application/devices/config/route.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 设备管理模块路由配置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 设备管理路由
|
||||
'devices/count' => 'devices/index/count', // 获取设备总数
|
||||
'devices/list' => 'devices/index/index', // 获取设备列表
|
||||
'devices/info/:id' => 'devices/index/read', // 获取设备详情
|
||||
'devices/add' => ['devices/index/save', ['method' => 'post']], // 添加设备
|
||||
'devices/update/:id' => ['devices/index/update', ['method' => 'put']], // 更新设备
|
||||
'devices/delete/:id' => ['devices/index/delete', ['method' => 'delete']], // 删除设备
|
||||
'devices/count_by_brand' => 'devices/index/countByBrand', // 按设备品牌统计数量
|
||||
'devices/count_by_status' => 'devices/index/countByStatus', // 按设备在线状态统计数量
|
||||
];
|
||||
378
Server/application/devices/controller/Index.php
Normal file
378
Server/application/devices/controller/Index.php
Normal file
@@ -0,0 +1,378 @@
|
||||
<?php
|
||||
namespace app\devices\controller;
|
||||
|
||||
use think\Controller;
|
||||
use app\devices\model\Device;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* 设备管理控制器
|
||||
*/
|
||||
class Index extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取设备总数
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
try {
|
||||
// 获取查询条件
|
||||
$where = [];
|
||||
|
||||
// 设备品牌
|
||||
$brand = Request::param('brand');
|
||||
if (!empty($brand)) {
|
||||
$where['brand'] = $brand;
|
||||
}
|
||||
|
||||
// 设备型号
|
||||
$model = Request::param('model');
|
||||
if (!empty($model)) {
|
||||
$where['model'] = $model;
|
||||
}
|
||||
|
||||
// 设备在线状态
|
||||
$alive = Request::param('alive');
|
||||
if (is_numeric($alive)) {
|
||||
$where['alive'] = $alive;
|
||||
}
|
||||
|
||||
// 租户ID
|
||||
$tenantId = Request::param('tenant_id');
|
||||
if (is_numeric($tenantId)) {
|
||||
$where['tenantId'] = $tenantId;
|
||||
}
|
||||
|
||||
// 分组ID
|
||||
$groupId = Request::param('group_id');
|
||||
if (is_numeric($groupId)) {
|
||||
$where['groupId'] = $groupId;
|
||||
}
|
||||
|
||||
// 获取设备总数
|
||||
$count = Device::getDeviceCount($where);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'count' => $count
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
try {
|
||||
// 获取查询条件
|
||||
$where = [];
|
||||
|
||||
// 设备名称
|
||||
$userName = Request::param('user_name');
|
||||
if (!empty($userName)) {
|
||||
$where['userName'] = ['like', "%{$userName}%"];
|
||||
}
|
||||
|
||||
// 设备IMEI
|
||||
$imei = Request::param('imei');
|
||||
if (!empty($imei)) {
|
||||
$where['imei'] = ['like', "%{$imei}%"];
|
||||
}
|
||||
|
||||
// 设备品牌
|
||||
$brand = Request::param('brand');
|
||||
if (!empty($brand)) {
|
||||
$where['brand'] = $brand;
|
||||
}
|
||||
|
||||
// 设备型号
|
||||
$model = Request::param('model');
|
||||
if (!empty($model)) {
|
||||
$where['model'] = $model;
|
||||
}
|
||||
|
||||
// 设备在线状态
|
||||
$alive = Request::param('alive');
|
||||
if (is_numeric($alive)) {
|
||||
$where['alive'] = $alive;
|
||||
}
|
||||
|
||||
// 租户ID
|
||||
$tenantId = Request::param('tenant_id');
|
||||
if (is_numeric($tenantId)) {
|
||||
$where['tenantId'] = $tenantId;
|
||||
}
|
||||
|
||||
// 分组ID
|
||||
$groupId = Request::param('group_id');
|
||||
if (is_numeric($groupId)) {
|
||||
$where['groupId'] = $groupId;
|
||||
}
|
||||
|
||||
// 获取分页参数
|
||||
$page = Request::param('page/d', 1);
|
||||
$limit = Request::param('limit/d', 10);
|
||||
|
||||
// 获取排序参数
|
||||
$sort = Request::param('sort', 'id');
|
||||
$order = Request::param('order', 'desc');
|
||||
|
||||
// 获取设备列表
|
||||
$list = Device::getDeviceList($where, "{$sort} {$order}", $page, $limit);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'total' => $list->total(),
|
||||
'list' => $list->items()
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function read()
|
||||
{
|
||||
try {
|
||||
// 获取设备ID
|
||||
$id = Request::param('id/d');
|
||||
if (empty($id)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '参数错误'
|
||||
]);
|
||||
}
|
||||
|
||||
// 获取设备详情
|
||||
$info = Device::getDeviceInfo($id);
|
||||
if (empty($info)) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '设备不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $info
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加设备
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
try {
|
||||
// 获取设备数据
|
||||
$data = Request::post();
|
||||
|
||||
// 验证IMEI是否为空
|
||||
if (empty($data['imei'])) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '设备IMEI不能为空'
|
||||
]);
|
||||
}
|
||||
|
||||
// 验证IMEI是否已存在
|
||||
$exists = Device::where('imei', $data['imei'])->where('isDeleted', 0)->find();
|
||||
if ($exists) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '设备IMEI已存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 添加设备
|
||||
$id = Device::addDevice($data);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '添加成功',
|
||||
'data' => [
|
||||
'id' => $id
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '添加失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
try {
|
||||
// 获取设备ID
|
||||
$id = Request::param('id/d');
|
||||
if (empty($id)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '参数错误'
|
||||
]);
|
||||
}
|
||||
|
||||
// 获取设备数据
|
||||
$data = Request::put();
|
||||
|
||||
// 验证设备是否存在
|
||||
$exists = Device::where('id', $id)->where('isDeleted', 0)->find();
|
||||
if (!$exists) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '设备不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 如果更新IMEI,验证IMEI是否已存在
|
||||
if (!empty($data['imei']) && $data['imei'] != $exists['imei']) {
|
||||
$imeiExists = Device::where('imei', $data['imei'])->where('isDeleted', 0)->where('id', '<>', $id)->find();
|
||||
if ($imeiExists) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '设备IMEI已存在'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新设备
|
||||
$result = Device::updateDevice($id, $data);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '更新成功',
|
||||
'data' => [
|
||||
'result' => $result
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '更新失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
// 获取设备ID
|
||||
$id = Request::param('id/d');
|
||||
if (empty($id)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '参数错误'
|
||||
]);
|
||||
}
|
||||
|
||||
// 验证设备是否存在
|
||||
$exists = Device::where('id', $id)->where('isDeleted', 0)->find();
|
||||
if (!$exists) {
|
||||
return json([
|
||||
'code' => 404,
|
||||
'msg' => '设备不存在'
|
||||
]);
|
||||
}
|
||||
|
||||
// 删除设备
|
||||
$result = Device::deleteDevice($id);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '删除成功',
|
||||
'data' => [
|
||||
'result' => $result
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '删除失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按设备品牌统计数量
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function countByBrand()
|
||||
{
|
||||
try {
|
||||
// 获取统计数据
|
||||
$data = Device::countByBrand();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $data
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按设备在线状态统计数量
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function countByStatus()
|
||||
{
|
||||
try {
|
||||
// 获取统计数据
|
||||
$data = Device::countByStatus();
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => $data
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
135
Server/application/devices/model/Device.php
Normal file
135
Server/application/devices/model/Device.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
namespace app\devices\model;
|
||||
|
||||
use think\Model;
|
||||
use think\Db;
|
||||
|
||||
/**
|
||||
* 设备模型类
|
||||
*/
|
||||
class Device extends Model
|
||||
{
|
||||
// 设置表名
|
||||
protected $name = 'tk_device';
|
||||
|
||||
// 设置主键
|
||||
protected $pk = 'id';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createTime';
|
||||
protected $updateTime = 'updateTime';
|
||||
protected $deleteTime = 'deleteTime';
|
||||
|
||||
/**
|
||||
* 获取设备总数
|
||||
* @param array $where 查询条件
|
||||
* @return int 设备总数
|
||||
*/
|
||||
public static function getDeviceCount($where = [])
|
||||
{
|
||||
// 默认只统计未删除的设备
|
||||
if (!isset($where['isDeleted'])) {
|
||||
$where['isDeleted'] = 0;
|
||||
}
|
||||
|
||||
return self::where($where)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表
|
||||
* @param array $where 查询条件
|
||||
* @param string $order 排序方式
|
||||
* @param int $page 页码
|
||||
* @param int $limit 每页数量
|
||||
* @return \think\Paginator 分页对象
|
||||
*/
|
||||
public static function getDeviceList($where = [], $order = 'id desc', $page = 1, $limit = 10)
|
||||
{
|
||||
// 默认只查询未删除的设备
|
||||
if (!isset($where['isDeleted'])) {
|
||||
$where['isDeleted'] = 0;
|
||||
}
|
||||
|
||||
return self::where($where)
|
||||
->order($order)
|
||||
->paginate($limit, false, ['page' => $page]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备详情
|
||||
* @param int $id 设备ID
|
||||
* @return array|null 设备信息
|
||||
*/
|
||||
public static function getDeviceInfo($id)
|
||||
{
|
||||
return self::where('id', $id)
|
||||
->where('isDeleted', 0)
|
||||
->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加设备
|
||||
* @param array $data 设备数据
|
||||
* @return int 新增设备ID
|
||||
*/
|
||||
public static function addDevice($data)
|
||||
{
|
||||
$device = new self();
|
||||
$device->allowField(true)->save($data);
|
||||
return $device->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备
|
||||
* @param int $id 设备ID
|
||||
* @param array $data 设备数据
|
||||
* @return bool 更新结果
|
||||
*/
|
||||
public static function updateDevice($id, $data)
|
||||
{
|
||||
return self::where('id', $id)
|
||||
->where('isDeleted', 0)
|
||||
->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备(软删除)
|
||||
* @param int $id 设备ID
|
||||
* @return bool 删除结果
|
||||
*/
|
||||
public static function deleteDevice($id)
|
||||
{
|
||||
return self::where('id', $id)
|
||||
->update([
|
||||
'isDeleted' => 1,
|
||||
'deleteTime' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按设备品牌统计数量
|
||||
* @return array 统计结果
|
||||
*/
|
||||
public static function countByBrand()
|
||||
{
|
||||
return self::where('isDeleted', 0)
|
||||
->group('brand')
|
||||
->field('brand, count(*) as count')
|
||||
->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按设备在线状态统计数量
|
||||
* @return array 统计结果
|
||||
*/
|
||||
public static function countByStatus()
|
||||
{
|
||||
return self::where('isDeleted', 0)
|
||||
->group('alive')
|
||||
->field('alive, count(*) as count')
|
||||
->select();
|
||||
}
|
||||
}
|
||||
49
Server/application/http/middleware/JwtAuth.php
Normal file
49
Server/application/http/middleware/JwtAuth.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace app\http\middleware;
|
||||
|
||||
use app\common\util\JwtUtil;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* JWT认证中间件
|
||||
*/
|
||||
class JwtAuth
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
// 获取Token
|
||||
$token = JwtUtil::getRequestToken();
|
||||
|
||||
// 验证Token
|
||||
if (!$token) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '未授权访问,缺少有效的身份凭证',
|
||||
'data' => null
|
||||
])->header(['Content-Type' => 'application/json; charset=utf-8']);
|
||||
}
|
||||
|
||||
$payload = JwtUtil::verifyToken($token);
|
||||
if (!$payload) {
|
||||
return json([
|
||||
'code' => 401,
|
||||
'msg' => '授权已过期或无效',
|
||||
'data' => null
|
||||
])->header(['Content-Type' => 'application/json; charset=utf-8']);
|
||||
}
|
||||
|
||||
// 将用户信息附加到请求中
|
||||
$request->userInfo = $payload;
|
||||
|
||||
// 写入日志
|
||||
Log::info('JWT认证通过', ['user_id' => $payload['id'] ?? 0, 'username' => $payload['username'] ?? '']);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ return [
|
||||
// 默认语言
|
||||
'default_lang' => 'zh-cn',
|
||||
// 应用类库后缀
|
||||
'class_suffix' => true,
|
||||
'class_suffix' => false,
|
||||
// 控制器类后缀
|
||||
'controller_suffix' => false,
|
||||
|
||||
@@ -54,9 +54,9 @@ return [
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 默认模块名
|
||||
'default_module' => 'frontend',
|
||||
'default_module' => 'index',
|
||||
// 禁止访问模块
|
||||
'deny_module_list' => ['common'],
|
||||
'deny_module_list' => [],
|
||||
// 默认控制器名
|
||||
'default_controller' => 'Index',
|
||||
// 默认操作名
|
||||
@@ -89,9 +89,9 @@ return [
|
||||
// IP代理获取标识
|
||||
'http_agent_ip' => 'X-REAL-IP',
|
||||
// URL伪静态后缀
|
||||
'url_html_suffix' => '',
|
||||
'url_html_suffix' => 'html',
|
||||
// URL普通方式参数 用于自动生成
|
||||
'url_common_param' => true,
|
||||
'url_common_param' => false,
|
||||
// URL参数方式 0 按名称成对解析 1 按顺序解析
|
||||
'url_param_type' => 0,
|
||||
// 是否开启路由延迟解析
|
||||
|
||||
@@ -28,11 +28,11 @@ return [
|
||||
// 数据库连接参数
|
||||
'params' => [],
|
||||
// 数据库编码默认采用utf8
|
||||
'charset' => 'utf8mb4',
|
||||
'charset' => env('database.charset', 'utf8mb4'),
|
||||
// 数据库表前缀
|
||||
'prefix' => Env::get('database.prefix', 'tk_'),
|
||||
// 数据库调试模式
|
||||
'debug' => true,
|
||||
'debug' => env('database.debug', true),
|
||||
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
|
||||
'deploy' => 0,
|
||||
// 数据库读写是否分离 主从式有效
|
||||
|
||||
@@ -15,4 +15,12 @@
|
||||
return [
|
||||
// 默认中间件命名空间
|
||||
'default_namespace' => 'app\\http\\middleware\\',
|
||||
|
||||
// 别名或分组
|
||||
'alias' => [
|
||||
'jwt' => 'JwtAuth',
|
||||
],
|
||||
|
||||
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
|
||||
'priority' => [],
|
||||
];
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks -Multiviews
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*)$ index.php [L]
|
||||
</IfModule>
|
||||
|
||||
76
Server/public/api_test.php
Normal file
76
Server/public/api_test.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// API测试入口文件
|
||||
|
||||
// 检查URL参数
|
||||
$url = isset($_GET['url']) ? $_GET['url'] : '';
|
||||
$method = isset($_GET['method']) ? strtoupper($_GET['method']) : 'GET';
|
||||
$data = isset($_GET['data']) ? $_GET['data'] : '{}';
|
||||
|
||||
// 将JSON字符串转为PHP数组
|
||||
$jsonData = json_decode($data, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$jsonData = [];
|
||||
}
|
||||
|
||||
// 获取token参数
|
||||
$token = isset($_GET['token']) ? $_GET['token'] : '';
|
||||
|
||||
echo '<h1>API测试工具</h1>';
|
||||
echo '<form method="get">';
|
||||
echo '<p>API路径: <input type="text" name="url" value="' . htmlspecialchars($url) . '" style="width:300px" placeholder="例如: api/auth/login"/></p>';
|
||||
echo '<p>请求方法: <select name="method">
|
||||
<option value="GET"' . ($method == 'GET' ? ' selected' : '') . '>GET</option>
|
||||
<option value="POST"' . ($method == 'POST' ? ' selected' : '') . '>POST</option>
|
||||
</select></p>';
|
||||
echo '<p>请求数据 (JSON): <textarea name="data" style="width:400px;height:100px">' . htmlspecialchars($data) . '</textarea></p>';
|
||||
echo '<p>Authorization Token: <input type="text" name="token" value="' . htmlspecialchars($token) . '" style="width:400px" placeholder="Bearer token..."/></p>';
|
||||
echo '<p><input type="submit" value="发送请求"/></p>';
|
||||
echo '</form>';
|
||||
|
||||
// 如果有URL参数,发送API请求
|
||||
if (!empty($url)) {
|
||||
// 构建完整URL
|
||||
$fullUrl = 'http://' . $_SERVER['HTTP_HOST'] . '/' . $url;
|
||||
|
||||
// 初始化cURL
|
||||
$ch = curl_init($fullUrl);
|
||||
|
||||
// 设置cURL选项
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
// 设置请求方法
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($jsonData));
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
$headers = ['Content-Type: application/json'];
|
||||
if (!empty($token)) {
|
||||
$headers[] = 'Authorization: ' . $token;
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// 执行请求
|
||||
$result = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
// 检查是否有错误
|
||||
if (curl_errno($ch)) {
|
||||
echo '<h2>请求错误</h2>';
|
||||
echo '<pre>' . htmlspecialchars(curl_error($ch)) . '</pre>';
|
||||
} else {
|
||||
echo '<h2>响应结果 (HTTP状态码: ' . $httpCode . ')</h2>';
|
||||
echo '<pre>' . htmlspecialchars($result) . '</pre>';
|
||||
|
||||
// 尝试解析JSON
|
||||
$jsonResult = json_decode($result, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
echo '<h2>格式化JSON响应</h2>';
|
||||
echo '<pre>' . htmlspecialchars(json_encode($jsonResult, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) . '</pre>';
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭cURL资源
|
||||
curl_close($ch);
|
||||
}
|
||||
123
Server/public/login.html
Normal file
123
Server/public/login.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>登录测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
#result {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: #f9f9f9;
|
||||
min-height: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>JWT登录测试</h1>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username">用户名:</label>
|
||||
<input type="text" id="username" value="admin">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">密码:</label>
|
||||
<input type="password" id="password" value="123456">
|
||||
</div>
|
||||
|
||||
<button id="loginBtn">登录</button>
|
||||
<button id="infoBtn">获取用户信息</button>
|
||||
|
||||
<div id="result">
|
||||
<p>响应结果将显示在这里</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('loginBtn').addEventListener('click', login);
|
||||
document.getElementById('infoBtn').addEventListener('click', getUserInfo);
|
||||
|
||||
let token = '';
|
||||
|
||||
async function login() {
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
document.getElementById('result').innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
|
||||
|
||||
if (data.code === 200 && data.data.token) {
|
||||
token = data.data.token;
|
||||
console.log('Token stored:', token);
|
||||
}
|
||||
} catch (error) {
|
||||
document.getElementById('result').innerHTML = `<p>Error: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserInfo() {
|
||||
if (!token) {
|
||||
document.getElementById('result').innerHTML = '<p>请先登录获取Token</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/info', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
document.getElementById('result').innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
|
||||
} catch (error) {
|
||||
document.getElementById('result').innerHTML = `<p>Error: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
8
Server/public/nginx.htaccess
Normal file
8
Server/public/nginx.htaccess
Normal file
@@ -0,0 +1,8 @@
|
||||
location ~* (runtime|application)/{
|
||||
return 403;
|
||||
}
|
||||
location / {
|
||||
if (!-e $request_filename){
|
||||
rewrite ^(.*)$ /index.php?s=$1 last; break;
|
||||
}
|
||||
}
|
||||
36
Server/public/test.php
Normal file
36
Server/public/test.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// 测试登录API
|
||||
|
||||
$url = 'http://localhost/api/auth/login';
|
||||
$data = json_encode([
|
||||
'username' => 'admin',
|
||||
'password' => '123456'
|
||||
]);
|
||||
|
||||
// 初始化cURL
|
||||
$ch = curl_init($url);
|
||||
|
||||
// 设置cURL选项
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($data)
|
||||
]);
|
||||
|
||||
// 执行请求
|
||||
$result = curl_exec($ch);
|
||||
|
||||
// 检查是否有错误
|
||||
if (curl_errno($ch)) {
|
||||
echo '请求错误: ' . curl_error($ch);
|
||||
} else {
|
||||
// 输出结果
|
||||
echo '<pre>';
|
||||
print_r(json_decode($result, true));
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
// 关闭cURL资源
|
||||
curl_close($ch);
|
||||
@@ -9,12 +9,15 @@
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
use think\facade\Route;
|
||||
|
||||
Route::get('think', function () {
|
||||
return 'hello,ThinkPHP5!';
|
||||
});
|
||||
|
||||
Route::get('hello/:name', 'index/hello');
|
||||
|
||||
return [
|
||||
// 加载Common模块路由配置
|
||||
include __DIR__ . '/../application/common/config/route.php';
|
||||
|
||||
];
|
||||
return [];
|
||||
|
||||
Reference in New Issue
Block a user