Compare commits

...

43 Commits

Author SHA1 Message Date
cansnow 271ec4b9c2 增加音乐,摇一摇,服务等页面 2026-02-15 19:41:13 +08:00
cansnow abd279e7a7 恢复视频的发布 2026-02-15 19:40:36 +08:00
cansnow 7c6656d1fc 350 2026-02-13 08:12:56 +08:00
cansnow 6720c15e30 27 2026-02-09 07:29:02 +08:00
cansnow 2860c46ec1 Update App.vue 2026-02-09 03:03:35 +08:00
cansnow 47d10945a6 26 2026-02-09 03:03:22 +08:00
cansnow 560b214af4 25 2026-02-08 16:27:14 +08:00
cansnow 1f4a588d3b 24 2026-01-30 14:45:45 +08:00
cansnow 35a41d8358 23 2026-01-20 21:14:57 +08:00
cansnow 37b53b54ff 增加群相册功能,长按朋友圈文字可以复制 2026-01-20 18:09:59 +08:00
cansnow db99bebcb4 22 2026-01-15 22:50:35 +08:00
commie dd16348558 admin revoke 2026-01-12 18:07:21 +08:00
cansnow e39c06526d 21 2026-01-11 15:48:41 +08:00
cansnow 2a0677014a 朋友圈 2026-01-11 14:00:09 +08:00
cansnow c38846f13b APP热更新 2026-01-11 13:51:16 +08:00
cansnow 941464c330 group mangage 2026-01-10 15:40:38 +08:00
cansnow 825ac3457d deletemsg 2026-01-09 20:22:25 +08:00
cansnow 7913a63a39 20 2026-01-09 09:15:59 +08:00
cansnow 78386d4cc1 19 2026-01-01 04:15:30 +08:00
cansnow 09c7889525 filecache 2025-12-27 07:08:30 +08:00
cansnow 974d149d25 remove img 2025-12-24 04:40:52 +08:00
cansnow f289f79813 18 2025-12-24 04:12:56 +08:00
cansnow f49f1f1ad1 17 2025-12-23 00:18:46 +08:00
cansnow 59d1ba9a7e 16 2025-12-17 09:13:15 +08:00
cansnow 2e793c3f77 15 2025-12-17 08:52:51 +08:00
cansnow cf1ad1c24b 14 2025-12-17 08:47:58 +08:00
cansnow 916cb22ecc Update index.vue 2025-12-11 22:33:43 +08:00
cansnow 5a086fa1fa 13 2025-12-11 22:33:31 +08:00
cansnow 375917f06c emoji 2025-12-09 09:27:29 +08:00
cansnow 4cb71e2b55 12 2025-12-08 18:18:20 +08:00
cansnow a0d44c1048 Update index.vue 2025-12-08 18:10:59 +08:00
cansnow b2e1b8930e 12 2025-12-08 18:10:51 +08:00
cansnow 22ee59cd3d 11 2025-12-08 02:29:46 +08:00
cansnow 69a61178e1 10 2025-12-05 16:10:52 +08:00
cansnow 29be534f22 9 2025-12-02 03:05:52 +08:00
cansnow b4c9ae1b67 8 2025-11-27 07:48:42 +08:00
cansnow ab625e6463 7 2025-11-27 07:40:32 +08:00
cansnow 198c3dd4a5 6 2025-11-27 03:55:38 +08:00
cansnow 1626f0c52a 5 2025-11-27 03:52:56 +08:00
commie b10e4b4336 4 2025-11-25 05:36:02 +08:00
commie 8e036cc171 3 2025-11-23 07:58:47 +08:00
commie 0783e46b4b 2 2025-11-23 01:01:52 +08:00
commie 6ec389ff41 1 2025-11-21 01:40:07 +08:00
3346 changed files with 114251 additions and 605751 deletions
+48
View File
@@ -0,0 +1,48 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
package-lock.json
.hbuilderx
uniCloud-aliyun
# plugin
/nativeplugins
# testing
/coverage
# production
/build
/unpackage/cache
/unpackage/debug
/unpackage/release
/unpackage/dist
/unpackage/resources
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# electron output
/dist
# db
OpenIM_*
# plugin
/nativeplugins
android-keeplive
sl-notify
+624
View File
@@ -0,0 +1,624 @@
<script>
import {mapGetters,mapActions} from "vuex";
// #ifdef APP
import IMSDK, {IMMethods,MessageType,SessionType,} from "openim-uniapp-polyfill";
// #endif
import config from "@/common/config";
import {getDbDir,toastWithCallback} from "@/util/common.js";
import {getConversationContent,conversationSort,prepareConversationState,updateTabbar} from "@/util/imCommon";
import {PageEvents,UpdateMessageTypes} from "@/constant";
import checkUpgrade from "@/util/app_update.js"
export default {
onLaunch: function() {
this.$store.dispatch("system/getConfig");
// #ifdef APP
plus.screen.lockOrientation("portrait-primary");
// #endif
// #ifdef H5
//screen.orientation.type = "";
screen.orientation.lock('portrait');
// #endif
// #ifdef MP
console.error(
`暂时不支持运行到小程序端`
);
return ;
// #endif
this.init();
},
onShow: function() {
//console.log("App Show");
// #ifdef APP
IMSDK.asyncApi(IMSDK.IMMethods.SetAppBackgroundStatus, IMSDK.uuid(), false);
// #endif
this.handleArguments();
//console.log(this.$store.state.contact);
},
onHide: function() {
//console.log("App Hide");
// #ifdef APP
IMSDK.asyncApi(IMSDK.IMMethods.SetAppBackgroundStatus, IMSDK.uuid(), true);
// #endif
},
computed: {
...mapGetters([
"storeConversationList",
"storeCurrentConversation",
"storeCurrentUserID",
"storeSelfInfo",
"storeRecvFriendApplications",
"storeRecvGroupApplications",
"storeHistoryMessageList",
"storeIsSyncing",
"storeGroupList",
"config",
'storeUnHandleFriendApplicationNum',
'storeUnHandleGroupApplicationNum',
'storeUnReadCount',
'storeCircleUnreadCount'
]),
},
watch:{
storeUnReadCount(){
updateTabbar(0);
},
storeUnHandleFriendApplicationNum(){
updateTabbar(1);
},
storeUnHandleGroupApplicationNum(){
updateTabbar(1);
},
storeCircleUnreadCount(){
updateTabbar(2);
}
},
methods: {
...mapActions("message", ["pushNewMessage", "updateOneMessage"]),
...mapActions("conversation", ["updateCurrentMemberInGroup"]),
...mapActions("circle", ["getFriendCircleInfo"]),
...mapActions("contact", [
"updateFriendInfo",
"pushNewFriend",
"updateBlackInfo",
"pushNewBlack",
"pushNewGroup",
"updateGroupInfo",
"pushNewRecvFriendApplition",
"updateRecvFriendApplition",
"pushNewSentFriendApplition",
"updateSentFriendApplition",
"pushNewRecvGroupApplition",
"updateRecvGroupApplition",
"pushNewSentGroupApplition",
"updateSentGroupApplition",
]),
init(){
//uni.setStorageSync('BusinessToken','eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3ZWJtYW4udGlueXdhbi5jbiIsImF1ZCI6IndlYm1hbi50aW55d2FuLmNuIiwiaWF0IjoxNzcwMTAyMjc2LCJuYmYiOjE3NzAxMDIyNzYsImV4cCI6MTc3MDcwNzA3NiwiZXh0ZW5kIjp7ImlkIjoxMDA3MDMsInVzZXJuYW1lIjoiMTg2MTE4ODI2MjMiLCJjbGllbnQiOiJXRUIifX0.yZ2u4cCZq2hLRAIpCKUs8GZkut7CObmfApIZGP_L2ro');
//uni.setStorageSync('IMToken','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJKTTZHMEVWMjVSIiwiUGxhdGZvcm1JRCI6MiwiZXhwIjoxNzc3ODc4Mjc2LCJpYXQiOjE3NzAxMDIyNzF9.rKWz00yCB36CexCahW-HhsFZfKIFcfkuOYLKApQWQT0 ');
//uni.setStorageSync('IMUserID','JM6G0EV25R');
const IMToken = uni.getStorageSync("IMToken");
const IMUserID = uni.getStorageSync("IMUserID")+'';
if (IMToken && IMUserID) {
// #ifdef APP
this.tryLogin();
// #endif
}else{
plus.navigator.closeSplashscreen();
//uni.$u.route("/pages/common/login/index");
}
},
setGlobalIMlistener() {
//console.log("setGlobalIMlistener");
// init
const kickHander = (message) => {
toastWithCallback(message, () => {
uni.removeStorage({
key: "IMToken",
});
uni.removeStorage({
key: "BusinessToken",
});
uni.$u.route("/pages/common/login/index");
});
};
//由于 APP 管理员强制用户下线,或由于登录策略导致用户被踢下线
IMSDK.subscribe(IMSDK.IMEvents.OnKickedOffline, (data) => {
kickHander("您的账号在其他设备登录,请重新登陆!");
});
//token无效回调。
IMSDK.subscribe(IMSDK.IMEvents.OnUserTokenExpired, (data) => {
kickHander("您的登录已过期,请重新登陆!");
});
IMSDK.subscribe(IMSDK.IMEvents.OnUserTokenInvalid, (data) => {
kickHander("您的登录已无效,请重新登陆!");
});
// sync
//向服务器同步会话开始时的回调。
const syncStartHandler = ({data}) => {
this.$store.commit("user/SET_IS_SYNCING", true);
this.$store.commit("user/SET_REINSTALL", data);
};
//同步中
const syncProgressHandler = ({data}) => {
this.$store.commit("user/SET_PROGRESS", data);
};
//向服务器同步会话成功时的回调。
const syncFinishHandler = () => {
uni.hideLoading();
this.$store.dispatch("conversation/getConversationList");
this.$store.dispatch("contact/getFriendList");
this.$store.dispatch("contact/getGrouplist");
this.$store.dispatch("conversation/getUnReadCount");
this.$store.commit("user/SET_IS_SYNCING", false);
};
//向服务器同步会话失败时的回调。
const syncFailedHandler = () => {
uni.hideLoading();
uni.$u.toast("同步消息失败");
this.$store.dispatch("conversation/getConversationList");
this.$store.dispatch("conversation/getUnReadCount");
this.$store.commit("user/SET_IS_SYNCING", false);
};
//向服务器同步会话开始时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerStart, syncStartHandler);
//向服务器同步会话成功时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerFinish, syncFinishHandler);
//向服务器同步会话失败时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerFailed, syncFailedHandler);
//同步中
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerProgress, syncProgressHandler);
// 当前登录用户个人信息改变时会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSelfInfoUpdated, ({data}) => {
this.$store.commit("user/SET_SELF_INFO", {
...this.storeSelfInfo,
...data,
});
});
// message
//接收到新消息时会收到此回调,回调中只会携带一条消息。
//设置了批量消息监听setBatchMsgListener时,此回调不会触发。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvNewMessage, ({data}) =>{});
IMSDK.subscribe(IMSDK.IMEvents.OnRecvNewMessages, ({data}) => {
if (this.storeIsSyncing) {
return;
}
console.log(data);
data.forEach(this.handleNewMessage);
});
//好友个人信息(包括备注)改变时会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendInfoChanged,({data}) => {
//console.log('friendInfoChangeHandler',data);
uni.$emit(IMSDK.IMEvents.OnFriendInfoChanged, {data});
this.updateFriendInfo({friendInfo: data,});
});
//两个用户成功建立好友关系后双方都会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendAdded, ({data}) => {
this.pushNewFriend(data);
});
//某个用户的好友列表减少时会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendDeleted, ({data}) => {
this.updateFriendInfo({
friendInfo: data,
isRemove: true,
});
});
// blacklist
//某个用户的黑名单列表增加时会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnBlackAdded, ({data}) => {
this.pushNewBlack(data);
});
//某个用户的黑名单列表减少时会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnBlackDeleted, ({data}) => {
this.updateBlackInfo({
blackInfo: data,
isRemove: true,
});
});
// group
const joinedGroupAddedHandler = ({data}) => {
this.pushNewGroup(data);
};
const joinedGroupDeletedHandler = ({data}) => {
this.updateGroupInfo({
groupInfo: data,
isRemove: true,
});
};
const groupInfoChangedHandler = ({data}) => {
this.updateGroupInfo({
groupInfo: data,
});
};
const groupMemberInfoChangedHandler = ({data}) => {
uni.$emit(IMSDK.IMEvents.OnGroupMemberInfoChanged, {data});
if (data.groupID === this.storeCurrentConversation?.groupID) {
this.updateCurrentMemberInGroup(data);
}
};
//用户所在群组的数量增加时(被邀请入群、入群申请被同意等),会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnJoinedGroupAdded,joinedGroupAddedHandler);
//用户所在群组的数量减少时(主动退群、群被解散等),会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnJoinedGroupDeleted,joinedGroupDeletedHandler);
//群组信息(头像、群名称等,也包括群主变化)改变时,该群所有群成员会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupInfoChanged,groupInfoChangedHandler);
//群成员信息改变(群昵称、头像等)后回调,该群所有群成员会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupMemberInfoChanged,groupMemberInfoChangedHandler);
// application
const friendApplicationNumHandler = ({data}) => {
const isRecv = data.toUserID === this.storeCurrentUserID;
if (isRecv) {
this.pushNewRecvFriendApplition(data);
} else {
this.pushNewSentFriendApplition(data);
}
};
const friendApplicationAccessHandler = ({data}) => {
console.log(data);
const isRecv = data.toUserID === this.storeCurrentUserID;
if (isRecv) {
this.updateRecvFriendApplition({
application: data,
});
} else {
this.updateSentFriendApplition({
application: data,
});
}
};
const groupApplicationNumHandler = ({data}) => {
const isRecv = data.userID !== this.storeCurrentUserID;
if (isRecv) {
this.pushNewRecvGroupApplition(data);
} else {
this.pushNewSentGroupApplition(data);
}
};
const groupApplicationAccessHandler = ({data}) => {
const isRecv = data.userID !== this.storeCurrentUserID;
if (isRecv) {
this.updateRecvGroupApplition({
application: data,
});
} else {
this.updateSentGroupApplition({
application: data,
});
}
};
//用户发起好友申请后,申请发起者和接收者都会收到此回调,接收者可以选择同意或拒绝好友申请。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendApplicationAdded,friendApplicationNumHandler);
//好友申请被同意时,申请发起方和接收方都会收到该回调,双方成功建立好友关系。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendApplicationAccepted,friendApplicationAccessHandler);
//好友申请被拒绝时,申请发起方和接收方都会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendApplicationRejected,friendApplicationAccessHandler);
//用户发起好友申请后,申请发起者和接收者都会收到此回调,接收者可以选择同意或拒绝好友申请。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupApplicationAdded,groupApplicationNumHandler);
//好友申请被同意时,申请发起方和接收方都会收到该回调,双方成功建立好友关系。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupApplicationAccepted,groupApplicationAccessHandler);
//好友申请被拒绝时,申请发起方和接收方都会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupApplicationRejected,groupApplicationAccessHandler);
//群组被解散时,该群所有群成员会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnGroupDismissed,({ data })=>{});
//群成员增加(如用户被邀请进群),其他群成员会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnGroupMemberAdded,({ data })=>{});
//群成员增加(如用户被邀请进群),群成员减少(如群成员退群), 其他群成员会收到此回调。。
//IMSDK.subscribe(IMSDK.IMEvents.OnGroupMemberDeleted,({ data })=>{});
const deleteLocalMsg = (clientMsgID)=>{
let list = this.storeHistoryMessageList;
//console.log(data);
list = list.filter((item)=>{
return item.clientMsgID != clientMsgID;
})
this.$store.commit('message/SET_HISTORY_MESSAGE_LIST',list);
}
//收到的消息被撤回或自己发出的消息被撤回时,会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnNewRecvMessageRevoked,({data})=>{
//console.log('onNewRecvMessageRevoked',res);
deleteLocalMsg(data.clientMsgID);
});
//自己发出的单聊消息被对方标记为已读后,消息发送者会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvC2CReadReceipt,({ data })=>{});
//自己发出的群聊消息被群成员标记为已读后,消息发送者和标记者均会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvGroupReadReceipt,({ data })=>{});
//当应用在后台运行,接收到新消息时,会收到该回调,回调中只会携带一条消息。
//设置了批量消息监听setBatchMsgListener时,此回调不会触发。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvOfflineNewMessage,({ data })=>{});
//当应用在后台运行,接收到新消息时,会收到该回调,回调中可能会携带多条消息。
IMSDK.subscribe(IMSDK.IMEvents.OnRecvOfflineNewMessages,({data})=>{
data.forEach(this.handleOfflineNewMessages);
});
IMSDK.subscribe(IMSDK.IMEvents.OnMsgDeleted,({data})=>{
deleteLocalMsg(data.clientMsgID)
});
//已订阅用户的在线状态发生变化时,会触发此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnUserStatusChanged,({ data })=>{});
//建立WebSocket连接失败返回后,触发此回调
//IMSDK.subscribe(IMSDK.IMEvents.OnConnectFailed,({ data })=>{});
//建立WebSocket连接成功返回后,触发此回调
//IMSDK.subscribe(IMSDK.IMEvents.OnConnectSuccess,({ data })=>{});
//建立WebSocket连接中,触发此回调
//IMSDK.subscribe(IMSDK.IMEvents.OnConnecting,({ data })=>{});
//正在输入状态回调。
//IMSDK.subscribe('onInputStatusChanged',({ data })=>{});
// conversation
const totalUnreadCountChangedHandler = ({data}) => {
if (this.storeIsSyncing) {
return;
}
this.$store.commit("conversation/SET_UNREAD_COUNT", data);
};
const newConversationHandler = ({data}) => {
if (this.storeIsSyncing) {
return;
}
const result = [...data, ...this.storeConversationList];
this.$store.commit(
"conversation/SET_CONVERSATION_LIST",
conversationSort(result)
);
};
const conversationChangedHandler = ({data}) => {
//console.log('conversationChangedHandler',data);
if (this.storeIsSyncing) {
return;
}
let filterArr = [];
//console.log(data);
const chids = data.map((ch) => ch.conversationID);
filterArr = this.storeConversationList.filter((tc) => !chids.includes(tc.conversationID));
const idx = data.findIndex((c) =>c.conversationID === this.storeCurrentConversation.conversationID);
if (idx !== -1){
this.$store.commit("conversation/SET_CURRENT_CONVERSATION",data[idx]);
}
const result = [...data, ...filterArr];
this.$store.commit("conversation/SET_CONVERSATION_LIST",conversationSort(result));
};
//会话总未读发生变化时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnTotalUnreadMessageCountChanged,totalUnreadCountChangedHandler);
//有新会话产生时,会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnNewConversation, newConversationHandler);
//某些会话的关键信息发生变化时,会触发该回调,例如会话的未读数发生变化,会话的最后一条消息发生变化等。
IMSDK.subscribe(IMSDK.IMEvents.OnConversationChanged,conversationChangedHandler);
},
async tryLogin() {
const _this = this;
const IMToken = uni.getStorageSync("IMToken");
const IMUserID = uni.getStorageSync("IMUserID")+'';
//console.log('IMToken:',IMToken);
//console.log('IMUserID:',IMUserID);
const path = await getDbDir();
//console.log('path:',path);
const IMConfig = {
systemType: "uni-app",
apiAddr: config.getApiUrl(), // SDK的API接口地址。如:http://xxx:10002
wsAddr: config.getWsUrl(), // SDK的websocket地址。如: ws://xxx:10001
dataDir: path, // 数据存储路径
logLevel: 6,
logFilePath: path,
isLogStandardOutput: true,
isExternalExtensions: false,
};
//console.log('IMConfig:',IMConfig);
const flag = await IMSDK.asyncApi(IMMethods.InitSDK, IMSDK.uuid(), IMConfig);
//console.log('flag:',flag);
if (!flag) {
plus.navigator.closeSplashscreen();
console.log('初始化IMSDK失败!');
uni.$u.toast("初始化IMSDK失败!");
return;
}
_this.setGlobalIMlistener();
// setTimeout(()=>{
// },1000);
let status;
do{
status = await IMSDK.asyncApi(IMSDK.IMMethods.GetLoginStatus,IMSDK.uuid());
//console.log(status);
}while(status == -1001);
if (status === 3) {
console.log('初始化,已经登录!');
_this.initStore();
return;
}
if (status === 1) {
IMSDK.asyncApi(IMSDK.IMMethods.Login, IMSDK.uuid(), {
userID: IMUserID,
token: IMToken,
})
.then(_this.initStore)
.catch((err) => {
console.log(err);
uni.removeStorage({
key: "IMToken",
});
uni.removeStorage({
key: "BusinessToken",
});
plus.navigator.closeSplashscreen();
});
}
},
handleOfflineNewMessages(newServerMsg) {
console.log(newServerMsg);
console.log( getConversationContent(newServerMsg));
uni.createPushMessage({
title:"您的朋友发来新的消息",
content:getConversationContent(newServerMsg),
payload:{
type:"msg",
data:newServerMsg
},
//icon:'',
//sound:'',
//cover:'false',
//delay:0,
//when:0,//消息上显示的提示时间
//channelId:"",
//category:"",
success(res){
//console.log(res);
},
fail(res){
//console.log(res);
},
complete(res){
//console.log(res);
}
});
},
handleNewMessage(newServerMsg) {
if (this.inCurrentConversation(newServerMsg)) {
if (
newServerMsg.contentType !== MessageType.TypingMessage &&
newServerMsg.contentType !== MessageType.RevokeMessage
) {
newServerMsg.isAppend = true;
this.pushNewMessage(newServerMsg);
setTimeout(() => uni.$emit(PageEvents.ScrollToBottom, true));
uni.$u.debounce(this.markConversationAsRead, 2000);
}
}else{
this.handleOfflineNewMessages(newServerMsg);
}
},
inCurrentConversation(newServerMsg) {
switch (newServerMsg.sessionType) {
case SessionType.Single:
return (
newServerMsg.sendID === this.storeCurrentConversation.userID ||
(newServerMsg.sendID === this.storeCurrentUserID &&
newServerMsg.recvID === this.storeCurrentConversation.userID)
);
case SessionType.WorkingGroup:
return newServerMsg.groupID === this.storeCurrentConversation.groupID;
case SessionType.Notification:
return newServerMsg.sendID === this.storeCurrentConversation.userID;
default:
return false;
}
},
markConversationAsRead() {
IMSDK.asyncApi(
IMSDK.IMMethods.MarkConversationMessageAsRead,
IMSDK.uuid(),
this.storeCurrentConversation.conversationID
);
},
initStore() {
const _this = this;
this.$store.dispatch("user/getSelfInfo");
this.$store.dispatch("conversation/getConversationList");
this.$store.dispatch("conversation/getUnReadCount");
this.$store.dispatch("contact/getBlacklist");
this.$store.dispatch("contact/getRecvFriendApplications");
this.$store.dispatch("contact/getSentFriendApplications");
this.$store.dispatch("contact/getRecvGroupApplications");
this.$store.dispatch("contact/getSentGroupApplications");
this.$store.dispatch("contact/getFriendList");
this.$store.dispatch("circle/getFriendCircleInfo");
if(true !== this.handleArguments()){
uni.switchTab({
url: "/pages/conversation/conversationList/index?isRedirect=true",
complete() {
_this.keppAlive();
_this.checkUpdate();
},
fail(e){
console.log(e);
}
});
}
},
// 验证是否升级
checkUpdate() {
const _this = this;
checkUpgrade();
},
keppAlive(){
// #ifdef APP-NVUE
uni.requestPermissions(['android.permission.RECEIVE_BOOT_COMPLETED'], (result) => {
if (result.granted) {
console.log('权限已获得');
} else {
console.log('权限被拒绝');
uni.showModal({
title: '权限申请',
content: '您需要授权后台运行权限才能正常使用该功能',
showCancel: false
});
}
});
// #endif
},
handleArguments(){
var args= plus.runtime.arguments;
if(args){
if(args.startsWith('shunliao://')){
console.log(args);
return ;
}
if(args.startsWith('{')){
const json = JSON.parse(args);
if(json.type == 'msg'){
if(this.inCurrentConversation(json.data)){
}else{
let conversation = this.storeConversationList.find((item) => item === json.data);
if(conversation){
plus.navigator.closeSplashscreen();
prepareConversationState(conversation);
return true;
}
}
}
return ;
}
// 处理args参数,如直达到某新页面等
console.log(args);
}
}
}
};
</script>
<style lang="scss">
@import "@/static/imfont/iconfont.css";
/*每个页面公共css */
text,view{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
@import "@/uni_modules/uview-ui/index.scss";
@import "@/styles/login.scss";
@import "@/styles/global.scss";
uni-page-body {
height: 100vh;
overflow: hidden;
}
.uni-tabbar .uni-tabbar__icon {
width: 28px !important;
height: 28px !important;
}
</style>
+123
View File
@@ -0,0 +1,123 @@
```
安卓App Key:7105a186f961b5d418914c5d1b12f6af
n1e5a6s6m7
```
```
conversation详情
{
"conversationID": "si_100003_100004",
"conversationType": 1,
"userID": "100004",
"groupID": "",
"showName": "1111",
"faceURL": "/static/img/avatar.png",
"recvMsgOpt": 2,
"unreadCount": 0,
"groupAtType": 0,
"latestMsg": "{\"clientMsgID\":\"748ac3ce532afd7dc6638ee13154d5f4\",\"serverMsgID\":\"00ba977273b73af7ae8be68e70b24100\",\"createTime\":1764798647940,\"sendTime\":1764798644531,\"sessionType\":1,\"sendID\":\"100004\",\"recvID\":\"100003\",\"msgFrom\":100,\"contentType\":101,\"senderPlatformID\":2,\"senderNickname\":\"131****1111\",\"senderFaceUrl\":\"/static/img/avatar.png\",\"seq\":17,\"isRead\":true,\"status\":2,\"attachedInfo\":\"null\",\"textElem\":{\"content\":\"馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆馃槆\"},\"attachedInfoElem\":{\"groupHasReadInfo\":{\"hasReadCount\":0,\"groupMemberCount\":0},\"isPrivateChat\":false,\"burnDuration\":0,\"hasReadTime\":0,\"isEncryption\":false,\"inEncryptStatus\":false}}",
"latestMsgSendTime": 1764798644531,
"draftText": "",
"draftTextTime": 0,
"isPinned": true,
"isPrivateChat": false,
"burnDuration": 0,
"isNotInGroup": false,
"updateUnreadCountTime": 0,
"attachedInfo": "",
"ex": "",
"maxSeq": 0,
"minSeq": 0,
"msgDestructTime": 0,
"isMsgDestruct": false
}
```
```
group detail
{
"groupID": "1793688611",
"groupName": "5676uy",
"notification": "",
"introduction": "",
"faceURL": "",
"createTime": 1764103081757,
"status": 0,
"creatorUserID": "100003",
"groupType": 2,
"ownerUserID": "100003",
"memberCount": 3,
"ex": "",
"attachedInfo": "",
"needVerification": 0,
"lookMemberInfo": 0,
"applyMemberFriend": 0,
"notificationUpdateTime": 0,
"notificationUserID": ""
}
```
```
group member
{
"groupID": "1793688611",
"userID": "100004",
"nickname": "131****1111",
"faceURL": "/static/img/avatar.png",
"roleLevel": 20,
"joinTime": 1764103081761,
"joinSource": 2,
"inviterUserID": "100003",
"muteEndTime": 0,
"operatorUserID": "100003",
"ex": "",
"attachedInfo": ""
}
```
```
user detail
{
"userID": "100003",
"nickname": "131****2222",
"faceURL": "/static/img/avatar.png",
"createTime": 1764100199726,
"ex": "",
"attachedInfo": "",
"globalRecvMsgOpt": 0,
"id": 100003,
"role_id": 0,
"parent_id": null,
"group_id": 0,
"username": "13122222222",
"sex": "1",
"email": null,
"region": "86",
"mobile": "13122222222",
"level": 1,
"birthday": null,
"bio": null,
"money": "0.0000000000",
"score": 0,
"currency1": "0.0000000000",
"currency2": "0.0000000000",
"currency3": "0.0000000000",
"currency4": "0.0000000000",
"currency5": "0.0000000000",
"currency6": "0.0000000000",
"currency7": "0.0000000000",
"currency8": "0.0000000000",
"currency9": "0.0000000000",
"maxsuccessions": 0,
"successions": 0,
"loginfailure": 0,
"prev_time": null,
"last_time": 1764833943,
"last_ip": "139.202.159.214",
"join_time": 1764100199,
"join_ip": "139.202.158.3",
"token": null,
"invite_code": "WBDFAQSL",
"online": 0,
"status": 1,
"created_at": 1764100199,
"updated_at": 1764833943,
"deleted_at": null
}
```
-38
View File
@@ -1,38 +0,0 @@
[app]
debug = true
[server]
port=8585
domain=www.wenjb.com
https=false
[mysql]
host =127.0.0.1
port = 3307
database = questionnaire
username = questionnaire
password = ba9b492bda93ad4d
charset = utf8mb4
collation = utf8mb4_general_ci
prefix = wa_
strict = true
engine =
[mongodb]
host = 127.0.0.1
port = 27017
database = questionnaire_m
username = commie
password = n1e5a6s6m7
[redis]
host = 127.0.0.1
port = 6379
database = 10
password = n1e5a6s6m7
prefix = q_
[mcp]
TRANSPORT=http
HOST=127.0.0.1
PORT=8080
PATH=mcp
TIMEOUT=60000
MEMORY_LIMIT=256M
MAX_EXECUTION_TIME=0
-10
View File
@@ -1,10 +0,0 @@
/runtime
/.idea
/.vscode
/vendor
*.log
.env
.user.ini
/config/site.php
/config/pay.php
/app/command/Test.php
@@ -1,124 +0,0 @@
<?php
namespace app\api\controller;
use support\Request;
use taoser\facade\Validate;
use app\model\Address as AddressModel;
use hg\apidoc\annotation as Apidoc;
/**
* 提现地址
*/
class AddressController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
/**
* 列表
* @Apidoc\Method("POST")
* @Apidoc\Query("network", type="string", require=true, desc="网络")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list()
{
$limit = (int)input('limit',10);
$page = (int)input('page',1);
$status = (int)input('status',-1);
$network = input('network');
$type = input('type');
$model = AddressModel::where('user_id',\support\Jwt\JwtToken::getCurrentId());
//->where('network','BEP-20');
if($type){
$model = $model->where('status',1);
}
if($network){
$model = $model->where('network',$network);
}
$list = $model->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 创建
* @Apidoc\Method("POST")
* @Apidoc\Param("network", type="string", require=true, desc="网络,BEP-20,TRC-20,ALIPAY,WECHAT",default="BEP-20")
* @Apidoc\Param("address", type="string", require=true, desc="地址")
* @Apidoc\Param("title", type="string", require=true, desc="名称")
* @Apidoc\Param("status", type="string", require=true, desc="状态,可选,1,0,默认1")
*/
public function create()
{
//captcha_verfiy('image','create_address');
//* @Apidoc\Param("code", type="string", require=true, desc="图形验证码 event=create_address")
//$trade_password = input('trade_password');
//\support\Jwt::verify_trade_password($trade_password);
//* @Apidoc\Param("trade_password", type="string", require=true, desc="交易密码")
$data = [
'network' => input('network','BEP-20'),
'title' => input('title'),
'address' => input('address'),
'img' => input('img'),
'is_default' => input('is_default'),
'status' => input('status',0),
'user_id' => \support\Jwt\JwtToken::getCurrentId()
];
if(!$data['title']){
return $this->error(__('Invalid title'));
}
if(!$data['address']){
return $this->error(__('Invalid address'));
}
AddressModel::create($data);
return $this->success(__('successful'));
}
/**
* 编辑
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string", require=true, desc="id")
* @Apidoc\Param("title", type="string", require=true, desc="名称")
* @Apidoc\Param("status", type="string", require=true, desc="状态,可选,1,0,默认1")
*/
public function update()
{
//captcha_verfiy('image','update_address');
//$trade_password = input('trade_password');
//\support\Jwt::verify_trade_password($trade_password);
$data = [
'id' => input('id',''),
'title' => input('title',''),
//'is_default' => input('is_default'),
'status' => input('status',1),
];
if(!$data['id']){
return $this->error(__('Invalid parameters'));
}
if(!$data['title']){
return $this->error(__('Invalid title'));
}
AddressModel::where('id',$data['id'])->save($data);
return $this->success(__('successful'));
}
/**
* 详情
* @Apidoc\Query("id", type="int", require=true, desc="id")
*/
public function detail(){
$appid = input('id');
$vo = AddressModel::where('id',$appid)->find();
if($vo) {
return $this->success(__('successful'),$vo->toArray());
}else{
return $this->error(__("Address is not exist"));
}
}
}
@@ -1,144 +0,0 @@
<?php
namespace app\api\controller;
use app\model\Archives as ArchivesModel;
use support\Request;
use taoser\facade\Validate;
use hg\apidoc\annotation as Apidoc;
/**
* 文章模块
*/
class ArticleController extends BaseController{
public $noNeedLogin = ['*'];
/**
* 列表
* @Apidoc\Query("category_id", type="int", require=true, desc="分类ID",default=10)
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list(){
$limit = (int)input('limit',10);
$category_id = (int)input('category_id',0);
$model = ArchivesModel::where('status','1')->where('type','article');
if($category_id){
$model = $model->where('category_id',$category_id);
}
$list = $model->order('id','desc')->paginate($limit);
$user_id=0;
try {
$user_id = \support\Jwt\JwtToken::getCurrentId();
} catch (\Throwable $th) {
}
$list->each(function($item)use($user_id){
if(!$user_id){
$item->is_read = 0;
}
$item->is_read = cache('article_read_'.$item->id.'_'.$user_id)?:0;
return $item;
});
return $this->success(__('successful'),$list->toArray());
}
/**
* faq
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function faq(){
$limit = (int)input('limit',10);
$model = ArchivesModel::alias('a')
->join('content c', 'a.id = c.id')
->where('a.status','1')
->where('a.type','article')
->where('a.category_id',9);
$list = $model->Field('a.title,a.id,c.content')->order('a.id','desc')->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 详情
* @Apidoc\Query("id", type="int", require=true, desc="ID")
*/
public function detail(){
$appid = input('id');
/** @var ArchivesModel $vo */
$vo = ArchivesModel::where('id',$appid)->find();
if($vo) {
$addon = \app\model\Content::where('id', $vo->id)->find()->toArray();
if ($addon) {
$vo->setAddonData($addon);
}
$user_id=0;
try {
$user_id = \support\Jwt\JwtToken::getCurrentId();
} catch (\Throwable $th) {
}
if($user_id){
cache('article_read_'.$vo->id.'_'.$user_id,1);
}
return $this->success(__('successful'),$vo->toArray());
}else{
return $this->error(__("Article does not exist"));
}
}
/**
* 单页详情
* @Apidoc\Query("id", type="int", require=true, desc="ID")
* @Apidoc\Query("name", type="string", require=true, desc="二选1")
*/
public function singpage(){
$appid = input('id');
$name = input('name');
/** @var ArchivesModel $vo */
if($name){
$vo = ArchivesModel::where('name',$name)->find();
}else{
if($appid){
$vo = ArchivesModel::where('id',$appid)->find();
}
}
if($vo) {
$addon = \app\model\Content::where('id', $vo->id)->find()->toArray();
if ($addon) {
$vo->setAddonData($addon);
}
return $this->success(__('successful'),$vo->toArray());
}else{
return $this->error(__("Article does not exist"));
}
}
/**
* 幻灯片
* @Apidoc\Query("id", type="int", require=true, desc="ID")
*/
public function slide(){
$list = [
['image'=>domain().'/storage/slide/1.jpg','title'=>''],
['image'=>domain().'/storage/slide/2.webp','title'=>''],
['image'=>domain().'/storage/slide/3.webp','title'=>''],
['image'=>domain().'/storage/slide/4.jpg','title'=>''],
];
return $this->success(__('successful'),$list);
}
/**
* 设为已读
* @Apidoc\Query("id", type="int", require=true, desc="ID,多个逗号隔开")
*/
function mask_as_read(){
$ids = input('id');
$user_id = \support\Jwt\JwtToken::getCurrentId();
if(!$user_id){
return $this->success(__('successful'));
}
$ids = explode(',',$ids);
foreach ($ids as $id) {
$key = 'article_read_'.$id.'_'.$user_id;
cache($key,1);
}
return $this->success(__('successful'));
}
}
@@ -1,49 +0,0 @@
<?php
namespace app\api\controller;
use app\model\User as UserModel;
use support\Request;
use hg\apidoc\annotation as Apidoc;
/**
* 余额日志
*/
class BalanceLogController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
/**
* 余额日志
* @Apidoc\Query("currency", type="string", require=true, desc="货币",default="money")
* @Apidoc\Query("type", type="string", require=true, desc="类型")
* @Apidoc\Query("startTime", type="string", require=true, desc="开始时间")
* @Apidoc\Query("endTime", type="string", require=true, desc="结束时间")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
function list(Request $request){
$user_id = \support\Jwt\JwtToken::getCurrentId();
$type = Input('type',0);
$currency = Input('currency','money');
$startTime = Input('startTime');
$endTime = Input('endTime');
$list = \app\model\BalanceLog::queryLogs($user_id,$currency,$type,$startTime,$endTime);
$BalanceTypeList= \app\enum\BalanceType::toArray();
$list->each(function($item)use($BalanceTypeList){
if($item->type == \app\enum\BalanceType::TRANSFER->value && $item->memo){
$item['target'] = UserModel::where('id',$item->memo)->value('username');
$item->memo = idEncode($item->memo);
}
$item->_type= $item->type;
$item->type= $BalanceTypeList[$item->type];
});
return $this->success(__('successful'),$list);
}
}
@@ -1,80 +0,0 @@
<?php
namespace app\api\controller;
use support\Request;
use support\Response;
use Shopwwi\WebmanFilesystem\FilesystemFactory;
use Shopwwi\WebmanFilesystem\Facade\Storage;
use hg\apidoc\annotation as Apidoc;
/**
* 基础控制器
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
class BaseController
{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = [];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
function __construct()
{
$this->_init();
}
protected function _init(){
}
/**
* 返回格式化json数据
*
* @param int $code
* @param string $msg
* @param array $data
* @return Response
*/
protected function json(int $code, string $msg = 'ok', array|object|null $data = []): Response
{
return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
}
protected function success(string $msg = '成功', array|object|null $data = []): Response
{
return $this->json(0, $msg, $data);
}
protected function fail(string $msg = '失败', array|object|null $data = []): Response
{
return $this->json(1,$msg, $data);
}
protected function error(string $msg = '失败', array|object|null $data = []): Response
{
return $this->json(1,$msg, $data);
}
/**
* @Apidoc\Title("上传")
* @Apidoc\Method("POST")
*/
function upload(Request $request)
{
//多文件上传
$files = $request->file();
try {
$result = Storage::adapter('public')
->path('upload/files')
->size(1024*1024*10)
->extYes(['image/jpeg','image/png'])
->uploads($files,0,1024*1024*100,false);
return $this->success(__('successful'),$result);
}catch (\Exception $e){
return $this->error($e->getMessage());
}
}
}
-123
View File
@@ -1,123 +0,0 @@
<?php
namespace app\api\controller;
use app\model\Card as CardModel;
use app\model\Cdkey as CdkeyModel;
use app\model\User as UserModel;
use support\think\Db;
use taoser\facade\Validate;
use hg\apidoc\annotation as Apidoc;
/**
* 卡密模块
*/
class CardController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
public $noNeedLogin = ['detail','list'];
/**
* 列表
* @Apidoc\Query("kw", type="string", require=false, desc="搜索关键字")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list(){
$limit = (int)input('limit',10);
$kw = (string)input('kw');
$model = CardModel::where('status',1);
if($kw){
$model = $model->whereLike('title',$kw);
}
$list = $model->order('id desc')->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 详情
* @Apidoc\Query("id", type="int", require=true, desc="ID")
* @Apidoc\Query("status", type="string", require=true, desc="状态")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function detail(){
$limit = (int)input('limit',10);
$id = (int)input('id',0);
$status = (string)input('status','all');
$model = CdkeyModel::where('category_id',$id);
if($status!='all'){
$model = $model->where('status',$status);
}
$list = $model->order('id desc')->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 详情
* @Apidoc\Query("id", type="int", require=true, desc="ID")
*/
public function download(){
$id = (int)input('id',0);
$model = CdkeyModel::where('category_id',$id);
$list = $model->order('id desc')->select();
return $this->success(__('successful'),$list->toArray());
}
/**
* 购买产品
* @Apidoc\Method("POST")
* @Apidoc\Param("title", type="string", require=true, desc="标题")
* @Apidoc\Param("total", type="int", require=true, desc="数量")
* @Apidoc\Param("days", type="int", require=true, desc="金额")
* @Apidoc\Param("trade_password", type="string", require=true, desc="交易密码")
*/
public function create()
{
$user = \support\Jwt::getUser();
$data = [
'user_id' => $user->id,
'title'=> input('title'),
'total' => (int)input('total'),
'days' => input('days'),
'status' => 0
];
if(!$data['title']){
return $this->error(__('Incorrect title'));
}
/** @var CardModel $gift */
if(!in_array($data['days'],[100,200,300,500])){
return $this->error(__('Incorrect amount'));
}
if($data['total'] < 1){
return $this->error(__('Incorrect quantity'));
}
$amount = $data['days'] * $data['total'];
$data['user_id'] = $user->id;
$data['amount'] = $amount;
$data['type'] = 'active';
$data['expires'] = strtotime('2030-12-31');
//验证交易密码
$trade_password = input('trade_password');
\support\Jwt::verify_trade_password($trade_password);
//var_dump($user);
//验证余额
if($data['amount'] < 1){
return $this->error(__('Incorrect amount'));
}
if($data['amount'] > $user->score){
return $this->error(__('Insufficient balance'));
}
Db::startTrans();
try{
$data = CardModel::create($data);
UserModel::score($data['user_id'],-$data['amount'],\app\enum\BalanceType::CDKEY,$data['id']);
Hook('card.create',$data);
Db::commit();
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
return $this->success(__('successful'));
}
}
@@ -1,448 +0,0 @@
<?php
namespace app\api\controller;
use taoser\facade\Validate;
use app\model\User as UserModel;
use support\Request;
use support\Response;
use Webman\Captcha\CaptchaBuilder;
use Webman\Captcha\PhraseBuilder;
use Shopwwi\WebmanFilesystem\FilesystemFactory;
use Shopwwi\WebmanFilesystem\Facade\Storage;
use hg\apidoc\annotation as Apidoc;
/**
* 公共接口
*/
class CommonController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = [];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = ['*'];
/**
* 加载初始化
*
* @Apidoc\Query("version", type="string", require=true, desc="版本号")
*/
public function init()
{
$lang = input('lang','en-US');
locale( $lang);
$config = Config('site');
$disallowFields = [
'api_token','reward_time_limit',
'mail_type','mail_smtp_host','mail_smtp_port','mail_smtp_user','mail_smtp_pass','mail_verify_type','mail_from',
'attachmentcategory','categorytype','cdkey_category','configgroup','flagtype',
'languages','forbiddenip','fixedpage','admin_login_captcha',
'mimetype','multipart','multiple','chunksize','classname','thumbstyle','previewtpl','timeout','maxsize','container',
'yeji_jicha_reward','suanli_rate','agent_expirs_retention','allow_currencys','allow_balance_log',
'agent_commission_total_rate','agent_commission_layer_rate','differential_commission_total_rate'
];
$config = array_diff_key($config, array_flip($disallowFields));
if(Request()->client != "web"){
$config["steps"] = Config('step');
}
$config['balance_type_list'] = \app\enum\BalanceType::toArray();
$config['recharge_status_list'] = \app\enum\RechargeStatus::toArray();
$config['withdrawl_status_list'] = \app\enum\WithdrawlStatus::toArray();
$config['server_status_list'] = \app\enum\ServerStatus::toArray();
return $this->success(__('successful'), $config);
}
/**
* test
* @Apidoc\Query("lang", type="string",require=true, desc="邮箱")
* @Apidoc\Method ("GET")
*/
function test(){
return $this->error(__('Invalid parameters'));
}
/**
* 注册会员
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱")
* @Apidoc\Param("password", type="string",require=true, desc="密码")
* @Apidoc\Param("trade_password", type="string",require=true, desc="交易密码")
* @Apidoc\Param("invite_code", type="string",require=true, desc="推荐码")
* @Apidoc\Param("code", type="string",require=true, desc="邮箱验证码,event=register")
*/
public function register()
{
$email = input('email');
$password = input('password');
$trade_password= input( 'trade_password');
$username = input('username');
$mobile = input('mobile');
$invite_code = input('invite_code');
if ($email && !Validate::is($email, "email")) {
return $this->error(__('Email is incorrect'));
}
if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) {
return $this->error(__('Mobile is incorrect'));
}
if(Config('site.user_register_way') == 'mobile'){
if (!$mobile) {
return $this->error(__('Invalid parameters'));
}
$username = $mobile;
captcha_verfiy('mobile','register',$mobile);
}else if(Config('site.user_register_way') == 'email'){
if (!$email) {
return $this->error(__('Invalid parameters'));
}
if ($email && !Validate::is($email, "email")) {
return $this->error(__('Email is incorrect'));
}
$username = $email;
captcha_verfiy('email','register',$email);
}else{
if (!$username) {
return $this->error(__('Invalid parameters'));
}
}
if (!$password) {
return $this->error(__('Invalid parameters'));
}
$extends = [
'role_id' => 0,
'group' => 0,
'avatar' => '/static/img/avatar.png',
];
// if (!$trade_password) {
// return $this->error(__('Invalid trade password'));
// }else{
// $extends['trade_password'] = \plugin\admin\app\common\Util::passwordHash($trade_password);
// }
//邀请码
if(!$invite_code){
return $this->error(__('Invalid invite code'));
}
if(strlen($invite_code) == 12){
//系统生产的一次性推荐吗
$inviteModel = \app\model\Invitecode::where('code',$invite_code)->find();
if(!$inviteModel){
return $this->error(__('错误的邀请码'));
}
$extends['group'] = 2;
$extends['role_id'] = 1;
$extends['parent_id'] = 0;
}else{
$inviter_user = UserModel::where('invite_code',$invite_code)->field('group,id')->find();
if(!$inviter_user){
return $this->error(__('Invalid invite code'));
}
$extends['parent_id'] = $inviter_user['id'];
}
try {
$user = \support\Jwt::register($username, $password, $email, $mobile, $extends);
if($inviteModel){
$inviteModel->status = 1;
$inviteModel->save();
}
$data = ['userinfo' => $user];
return $this->success(__('Sign up successful'), $data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 登录
* @Apidoc\Method("POST")
* @Apidoc\Param("username", type="string",require=true, desc="用户名")
* @Apidoc\Param("password", type="string",require=true, desc="密码")
*/
public function login(Request $request){
$username = input('username');
$password = input('password');
if (!$username || !$password) {
return $this->fail(__('Invalid username or password'));
}
try{
$user = \support\Jwt::login($username, $password,'username');
if($user === false){
return $this->fail(\support\Jwt::getError());
}
$user= Hook('user.profile',$user);
return $this->success(__('successful'), $user[0]);
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/**
* 退出登录
* @Apidoc\Method("GET")
*/
public function logout(){
\support\Jwt::logout();
return $this->success(__('successful'));
}
/**
* 重置密码
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱")
* @Apidoc\Param("newpassword", type="string",require=true, desc="新密码")
* @Apidoc\Param("code", type="string",require=true, desc="邮箱验证码,event=resetpwd")
*/
public function resetpwd()
{
$email = input("email");
$mobile = input("mobile");
$newpassword = input("newpassword");
if (!$newpassword) {
return $this->error(__('Invalid parameters'));
}
//验证Token
if (!Validate::check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,30}'])) {
return $this->error(__('Password must be 6 to 30 characters'));
}
if (!$mobile && !$email){
try{
$user = \support\Jwt::getUser();
}catch(\Exception $e){
log_alert($e->getMessage());
$user = false;
}
if($user){
captcha_verfiy('mobile','reset_trade_pwd',$user->mobile);
}
}else{
if ($email && Validate::is($email, "email")) {
captcha_verfiy('email','reset_trade_pwd',$email);
$user = UserModel::getByEmail($email);
}
if ($mobile && Validate::regex($mobile, "^1\d{10}$")) {
captcha_verfiy('mobile','reset_trade_pwd',$mobile);
$user = UserModel::getByMobile($mobile);
}
}
if (!$user) {
return $this->error(__('Invalid parameters'));
}
//模拟一次登录,需不需要充值登录信息?????
//\support\Jwt::direct($user->id);
try{
UserModel::where('id',$user->id)->save([
'loginfailure' => 0,
'password' => \plugin\admin\app\common\Util::passwordHash($newpassword)
]);
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
return $this->success(__('Reset password successful'));
}
/**
* 重置交易密码
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱")
* @Apidoc\Param("newpassword", type="string",require=true, desc="新密码")
* @Apidoc\Param("code", type="string",require=true, desc="邮箱验证码,event=reset_trade_pwd")
*/
public function reset_trade_pwd()
{
$email = input("email");
$mobile = input("mobile");
$newpassword = input("newpassword");
if (!$newpassword) {
return $this->error(__('Invalid parameters'));
}
//验证Token
if (!Validate::check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,6}'])) {
return $this->error(__('Trade password must be 6 characters'));
}
if (!$mobile && !$email){
try{
$user = \support\Jwt::getUser();
}catch(\Exception $e){
log_alert($e->getMessage());
$user = false;
}
if($user){
captcha_verfiy('mobile','reset_trade_pwd',$user->mobile);
}
}else{
if ($email && Validate::is($email, "email")) {
captcha_verfiy('email','reset_trade_pwd',$email);
$user = UserModel::getByEmail($email);
}
if ($mobile && Validate::regex($mobile, "^1\d{10}$")) {
captcha_verfiy('mobile','reset_trade_pwd',$mobile);
$user = UserModel::getByMobile($mobile);
}
}
if (!$user) {
return $this->error(__('Invalid parameters'));
}
//模拟一次登录,需不需要充值登录信息?????
//\support\Jwt::direct($user->id);
try{
log_alert($user->id.' 重置交易密码'.$newpassword);
UserModel::where('id',$user->id)->save([
'trade_password' => \plugin\admin\app\common\Util::passwordHash($newpassword)
]);
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
return $this->success(__('Reset Trade password successful'));
}
/**
* 验证码
* @Apidoc\Method ("POST")
* @Apidoc\Param("type", type="string",require=true, desc="GET参数,类型,email:邮箱验证码,image:图片验证码")
* @Apidoc\Param("event", type="string",require=true, desc="事件,regiser:注册,resetpwd:重置密码,withdrawl:提现")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱,可选")
*/
public function captcha(Request $request){
$request->input('type');
$type = $request->input('type');
$event = $request->input('event');
if($type == 'email'){
$email = $request->input('email');
if(!$email){
try {
$user = \support\Jwt::getUser();
$email = $user->email;
} catch (\Exception $th) {
return $this->error(__('Incoret param'));
}
}
$key = 'captcha_'.$event.'_'.$email;
$list = cache($key);
$list = $list ?:[];
$expris = 60;
if(cache('?exp_'.$key)){
if(cache('exp_'.$key)+$expris > time()){
return $this->fail(__('Only one verification code can be sent within %second% seconds',['%second%'=>$expris]));
}
}
$code =\support\Random::numeric(4);
$list[$code] = time();
cache($key,$list);
cache('exp_'.$key,time());
addJob([
'email' => $email,
'title' => __("Mt email code"),
'event' => $event,
'code' => $code
],'Email');
return $this->success(__('Email sent successfully'));
}elseif($type == 'mobile'){
$mobile = $request->input('mobile');
if(!$mobile){
try {
$user = \support\Jwt::getUser();
$mobile = $user->mobile;
} catch (\Exception $th) {
return $this->error(__('Incoret param'));
}
}
if (!Validate::regex($mobile, "^1\d{10}$")) {
return $this->error(__('Mobile is incorrect'));
}
$key = 'captcha_'.$event.'_'.$mobile;
$list = cache($key);
$list = $list ?:[];
$expris = 60;
if(cache('?exp_'.$key)){
if(cache('exp_'.$key)+$expris > time()){
return $this->fail(__('Only one verification code can be sent within %second% seconds',['%second%'=>$expris]));
}
}
$code =\support\Random::numeric(4);
$list[$code] = time();
cache($key,$list);
cache('exp_'.$key,time());
addJob([
'mobile' => $mobile,
'event' => $event,
'code' => $code
],'Sms');
return $this->success(__('SMS sent successfully'));
}else{
//TODO 图像验证码没有唯一的KEY
$key = 'captcha_'.$event.'_';
//abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ
$builder = new PhraseBuilder(4, '0123456789');
$captcha = new CaptchaBuilder(null, $builder);
$captcha->build(120);
$code = strtolower($captcha->getPhrase());
$list[$code] = time();
cache($key,$list);
if($request->method() =='GET'){
$img_content = $captcha->get();
return response($img_content, 200, ['Content-Type' => 'image/jpeg']);
}else{
$img_content = $captcha->inline();
return json([
'code' => 0,
'msg' => __('successful'),
'data' => $img_content
]);
}
}
}
/**
* 校验验证码
* @Apidoc\Param("type", type="string",require=true, desc="GET参数,类型,email:邮箱验证码,image:图片验证码")
* @Apidoc\Param("event", type="string",require=true, desc="事件,register:注册,resetpwd:重置密码,withdrawl:提现")
* @Apidoc\Param("email", type="string",require=false, desc="邮箱,可选,仅type==email时必填")
* @Apidoc\Param("code", type="string",require=true, desc="验证码")
*/
public function verify_captcha(Request $request): Response
{
$email = $request->post('email');
$mobile = $request->input('mobile');
$event = $request->post('event');
try {
if($email){
$result = captcha_verfiy('email', $event , $email,false);
}elseif($mobile){
$result = captcha_verfiy('mobile', $event , $mobile,false);
}else{
$result = captcha_verfiy('image', $event , '',false);
}
if(!$result){
return $this->fail(__('Captcha is incorrect'));
}
} catch (\Exception $e) {
return $this->fail($e->getMessage());
}
return $this->success(__('successful'));
}
/**
* @Apidoc\Title("上传")
* @Apidoc\Method("POST")
*/
function upload(Request $request)
{
//多文件上传
$files = $request->file();
try {
$result = Storage::adapter('public')
->path('upload/files')
->size(1024*1024*10)
->extYes(['image/jpeg','image/png'])
->uploads($files,0,1024*1024*100,false);
return $this->success(__('successful'),$result);
}catch (\Exception $e){
return $this->error($e->getMessage());
}
}
}
-159
View File
@@ -1,159 +0,0 @@
<?php
namespace app\api\controller;
use app\model\Gift as GiftModel;
use app\model\GiftOrder as GiftOrderModel;
use app\model\User as UserModel;
use support\think\Db;
use taoser\facade\Validate;
use hg\apidoc\annotation as Apidoc;
/**
* 礼品模块
*/
class GiftController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
public $noNeedLogin = ['detail','list'];
/**
* 列表
* @Apidoc\Query("kw", type="string", require=false, desc="搜索关键字")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list(){
$limit = (int)input('limit',10);
$model = GiftModel::where('status',1);
$list = $model->order('weight desc,id asc')->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 详情
* @Apidoc\Query("id", type="int", require=true, desc="ID")
*/
public function detail(){
try{
$user = \support\Jwt::getUser();
}catch(\Exception $e){
$user = ['id'=>0,'role_id'=>0];
}
$appid = input('id');
if(!$appid){
return $this->error(__("Product does not exist"));
}
/** @var GiftModel $gift */
$gift = GiftModel::where('id',$appid)->find();
//->cache(true,86400,'product_detail')
if(!$gift) {
return $this->error(__("Product does not exist"));
}
if($user['id']){
/** @var GiftOrderModel $total_quantity_user */
$total_quantity_user = GiftOrderModel::where('gift_id',$gift->id)->where('user_id',$user['id'])->sum('quantity');
$total_quantity_system = $gift->user_quantity ?: 99999999;
$max_quantity = $total_quantity_system-$total_quantity_user;
$max_quantity= $max_quantity < 0 ? 0: $max_quantity;
$gift->max_quantity = $max_quantity;
$gift->total_quantity_user = $total_quantity_user;
}else{
$gift->total_quantity_user = 0;
$gift->max_quantity = 0;
}
return $this->success(__('successful'),$gift->toArray());
}
/**
* 列表
* @Apidoc\Query("status", type="int", require=false, desc="状态")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function record(){
$limit = (int)input('limit',10);
$page = (int)input('page',1);
$status = input('status','all');
$user_id = \support\Jwt\JwtToken::getCurrentId();
$model = GiftOrderModel::with(['gift'])->where('user_id',$user_id);
if($status!='all'){
$model = $model->where('status',$status);
}
$list = $model->order('created_at desc')
->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 购买产品
* @Apidoc\Method("POST")
* @Apidoc\Param("gift_id", type="string", require=true, desc="产品ID")
* @Apidoc\Param("quantity", type="int", require=true, desc="数量")
* @Apidoc\Param("trade_password", type="string", require=true, desc="交易密码")
*/
public function create()
{
$user = \support\Jwt::getUser();
$data = [
'user_id' => $user->id,
'gift_id'=> input('gift_id'),
'quantity' => (int)input('quantity'),
'denomination' => input('denomination',0),
'status' => 0
];
if(!$data['gift_id']){
return $this->error(__('Gift is incorrect'));
}
/** @var GiftModel $gift */
$gift = GiftModel::where('id',$data['gift_id'])->find();
if(!$gift){
return $this->error(__('Gift is incorrect'));
}
// if(!in_array($data['denomination'],$gift->amounts)){
// return $this->error(__('Denomination is incorrect'));
// }
if($data['quantity'] < 1){
return $this->error(__('Quantity is incorrect'));
}
if($gift->stock < $data['quantity']){
return $this->error(__('Gift stock is insufficient'));
}
//$data['amount'] = $data['denomination'] * $data['quantity'];
$data['denomination'] = $gift->price;
$data['amount'] = $gift->price * $data['quantity'];
$total_quantity_user = GiftOrderModel::where('gift_id',$gift->id)->where('user_id',$user['id'])->sum('quantity');
$total_quantity_system = $gift->user_quantity ?: 99999999;
$can_purchase = $total_quantity_system-$total_quantity_user;
$can_purchase= $can_purchase < 0 ? 0: $can_purchase;
if($can_purchase < $data['quantity']){
return $this->error(__('You can only purchase %max_quantity% copies',[
'%max_quantity%' => $can_purchase
]));
}
//验证交易密码
$trade_password = input('trade_password');
\support\Jwt::verify_trade_password($trade_password);
//var_dump($user);
//验证余额
if($data['amount'] > $user->score){
return $this->error(__('Insufficient balance'));
}
Db::startTrans();
try{
$data = GiftOrderModel::create($data);
$gift->stock = $gift->stock - $data['quantity'];
$gift->sales = $gift->sales + $data['quantity'];
$gift->save();
UserModel::score($data['user_id'],-$data['amount'],\app\enum\BalanceType::GIFT_BUY,$data['id']);
Hook('gift.buy',$data);
Db::commit();
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
return $this->success(__('successful'));
}
}
@@ -1,17 +0,0 @@
<?php
namespace app\api\controller;
use Response;
use support\Request;
use hg\apidoc\annotation as Apidoc;
/**
* 主控制器
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
class IndexController extends BaseController{
public $noNeedLogin=['index','chart'];
public function index(){
return $this->success(__("Unverified address"));
}
}
@@ -1,69 +0,0 @@
<?php
namespace app\api\controller;
use app\model\Product as ProductModel;
use app\model\ProductOrder as ProductOrderModel;
use support\think\Db;
use taoser\facade\Validate;
use hg\apidoc\annotation as Apidoc;
/**
* 产品模块
*/
class ProductController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
public $noNeedLogin = ['detail','list'];
/**
* 列表
* @Apidoc\Query("kw", type="string", require=false, desc="搜索关键字")
* @Apidoc\Query("step", type="string", require=false, desc="类型,progress,done")
* @Apidoc\Query("billing_cycle", type="int", require=true, desc="周期",default=1)
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list(){
$limit = (int)input('limit',10);
$model = ProductModel::where('status',1);
$list = $model->order('weight desc,id asc')->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 详情
* @Apidoc\Query("id", type="int", require=true, desc="ID")
*/
public function detail(){
try{
$user = \support\Jwt::getUser();
}catch(\Exception $e){
$user = ['id'=>0,'role_id'=>0];
}
$appid = input('id');
if(!$appid){
return $this->error(__("Product does not exist"));
}
/** @var ProductModel $product */
$product = ProductModel::where('id',$appid)->find();
//->cache(true,86400,'product_detail')
if(!$product) {
return $this->error(__("Product does not exist"));
}
if($user['id']){
$total_quantity_user = ProductOrderModel::where('product_id',$product->id)->where('user_id',$user['id'])->sum('quantity');
$total_quantity_system = $product->user_quantity ?: 99999999;
$max_quantity = $total_quantity_system-$total_quantity_user;
$max_quantity= $max_quantity < 0 ? 0: $max_quantity;
$product->max_quantity = $max_quantity;
$product->total_quantity_user = $total_quantity_user;
}else{
$product->total_quantity_user = 0;
$product->max_quantity = 0;
}
return $this->success(__('successful'),$product->toArray());
}
}
@@ -1,145 +0,0 @@
<?php
namespace app\api\controller;
use app\model\ProductOrder as ProductOrderModel;
use app\model\Product as ProductModel;
use app\model\User as UserModel;
use support\think\Db;
use taoser\facade\Validate;
use hg\apidoc\annotation as Apidoc;
/**
* 我的产品
*/
class ProductOrderController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
public $noNeedLogin = [];
/**
* 列表
* @Apidoc\Query("step", type="int", require=false, desc="工作状态")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list(){
$limit = (int)input('limit',10);
$page = (int)input('page',1);
$step = (int)input('step',0);
$user_id = \support\Jwt\JwtToken::getCurrentId();
$model = ProductOrderModel::with(['product'])->where('user_id',$user_id);
if($step){
$ids = \app\model\WorkRecord::where('user_id',$user_id)
->distinct(true)
->where('status',$step)
->column('order_id');
$model = $model->whereIn('id',$ids);
}
$list = $model->where('status',1)
->order('created_at desc')
->paginate($limit);
$list->each(function($item)use($step){
//$_step=1;
if($step){
$_step = $step;
}else{
$_step = \app\model\WorkRecord::where('order_id',$item->id)
->order('status','asc')
->limit(0,1)
->value('status');
}
$item->step = $_step;
return $item;
});
return $this->success(__('successful'),$list->toArray());
}
/**
* 购买产品
* @Apidoc\Method("POST")
* @Apidoc\Param("product_id", type="string", require=true, desc="产品ID")
* @Apidoc\Param("quantity", type="int", require=true, desc="数量")
* @Apidoc\Param("trade_password", type="string", require=true, desc="交易密码")
*/
public function create()
{
$user = \support\Jwt::getUser();
//验证交易密码
$trade_password = input('trade_password');
\support\Jwt::verify_trade_password($trade_password);
$data = [
'user_id' => $user->id,
'product_id'=> input('product_id'),
'quantity' => (int)input('quantity'),
'accelerate' => input('accelerate'),
'status' => 1
];
if(!$data['product_id']){
return $this->error(__('Product is incorrect'));
}
if($data['quantity'] < 1){
return $this->error(__('Quantity is incorrect'));
}
if($data['product_id'] == 12){
$exsit = ProductOrderModel::where('product_id',$data['product_id'])->where('user_id',$data['user_id'])->count('id');
if($exsit != 0){
return $this->error(__('微量包每个用户只能购买一个'));
}
if($data['quantity'] != 1){
return $this->error(__('微量包每个用户只能购买一个'));
}
}
/** @var ProductModel $product */
$product = ProductModel::where('id',$data['product_id'])->find();
if(!$product){
return $this->error(__('Product is incorrect'));
}
/*
if ($product->start_time > time() || $product->end_time < time()) {
return $this->error(__('Can\'t buy now'));
}
*/
//return $this->success(__('successful'),[$product->start_time]);
$data['price'] = $product['price'];
if($data['accelerate']){
$data['price'] += $product['accelerate_price'];
$data['accelerate_times'] = $product['accelerate_assign_times'];
$data['accelerate_used'] = 0;
}
$data['amount'] = $data['price'] * $data['quantity'];
$data['total'] = $product['total'] * $data['quantity'];
$data['assigned'] = 0;
/*
$total_quantity_user = ProductOrderModel::where('product_id',$product->id)->where('user_id',$user['id'])->sum('quantity');
$total_quantity_system = $product->user_quantity ?: 99999999;
$can_purchase = $total_quantity_system-$total_quantity_user;
$can_purchase= $can_purchase < 0 ? 0: $can_purchase;
if($can_purchase < $data['quantity']){
return $this->error(__('You can only purchase %max_quantity% copies',[
'%max_quantity%' => $can_purchase
]));
}
*/
//var_dump($user);
//验证余额
if($data['amount'] > $user->money){
return $this->error(__('Insufficient balance'));
}
Db::startTrans();
try{
$data = ProductOrderModel::create($data);
$product->sales = $product->sales + $data['quantity'];
$product->save();
UserModel::money($data['user_id'],-$data['amount'],\app\enum\BalanceType::PRODUCT_BUY,$data['id']);
Hook('product.buy',$data);
Db::commit();
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
return $this->success(__('successful'));
}
}
@@ -1,251 +0,0 @@
<?php
namespace app\api\controller;
use app\model\WorkRecord as WorkRecordModel;
use app\model\Questionnaire as QuestionnaireModel;
use app\model\Product as ProductModel;
use app\model\ProductOrder as ProductOrderModel;
use app\model\BalanceLog;
use support\think\Db;
use taoser\facade\Validate;
use hg\apidoc\annotation as Apidoc;
/**
* 问卷
*/
class QuestionnaireController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
public $noNeedLogin = [];
/**
* 简介
* @Apidoc\Method("GET")
*/
public function info(){
$user = \support\Jwt::getUser();
return $this->success(__('successful'),[
'success_count' => WorkRecordModel::where('status',\app\enum\ServerStatus::COMPLETE->value)
->where('user_id',$user->id)
->count('id'),
'audit_count' => WorkRecordModel::where('status',\app\enum\ServerStatus::AUDITING->value)
->where('user_id',$user->id)
->count('id'),
]);
}
/**
* 列表
* @Apidoc\Query("kw", type="string", require=false, desc="搜索关键字")
* @Apidoc\Query("country", type="string", require=false, desc="国家,i18n编码")
* @Apidoc\Query("category_id", type="int", require=false, desc="分类")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list(){
$limit = (int)input('limit',10);
$model = QuestionnaireModel::with(['category'])->where('status',1);
if($category_id = input('category_id')){
$model = $model->where('category_id', $category_id);
}
if($country = input('country')){
$model = $model->where('country', $country);
}
$list = $model->order('id desc')->paginate($limit);
$list = $list->toArray();
foreach($list['data'] as $k=>$item){
$list['data'][$k]['id'] = idEncode($item['id']);
}
return $this->success(__('successful'),$list);
}
/**
* 问卷详情
* @Apidoc\Query("id", type="int", require=true, desc="ID")
*/
public function detail(){
try{
$user = \support\Jwt::getUser();
}catch(\Exception $e){
$user = ['id'=>0,'role_id'=>0];
}
$appid = input('id');
if(!$appid){
return $this->error(__("Product does not exist"));
}
/** @var ProductModel $product */
$product = ProductModel::where('id',$appid)->find();
//->cache(true,86400,'product_detail')
if(!$product) {
return $this->error(__("Product does not exist"));
}
if($user['id']){
$total_quantity_user = ProductOrderModel::where('product_id',$product->id)->where('user_id',$user['id'])->sum('quantity');
$total_quantity_system = $product->user_quantity ?: 99999999;
$max_quantity = $total_quantity_system-$total_quantity_user;
$max_quantity= $max_quantity < 0 ? 0: $max_quantity;
$product->max_quantity = $max_quantity;
$product->total_quantity_user = $total_quantity_user;
}else{
$product->total_quantity_user = 0;
$product->max_quantity = 0;
}
return $this->success(__('successful'),$product->toArray());
}
/**
* 领取问卷
* @Apidoc\Method("GET")
*/
function claim(){
$user = \support\Jwt::getUser();
//判断是否有问卷可领取
if($user->currency6<=0){
return $this->success(__('successful'));
}
//产生工作记录
$datas = [];
$questionnaire_ids = QuestionnaireModel::where('status',1)
->whereTime('start_time','<',time())
->whereTime('end_time','>',time())
->column('id');
$time = time();
for ($i=0; $i < $user->currency6; $i++) {
//随机选取一份问卷
$questionnaire_id = $questionnaire_ids[array_rand($questionnaire_ids)];
/** @var QuestionnaireModel $questionnaire */
$questionnaire = QuestionnaireModel::field('id,score')->find($questionnaire_id);
array_push($datas,[
"user_id" => $user->id,
"product_id" => null,
"questionnaire_id" => $questionnaire->id,
"order_id" => null,
"income" => $questionnaire->score,
"start_time" => 0,
"end_time" => 0,
"status" => 0, //自动开始
"created_at" => $time,
]);
}
Db::startTrans();
try {
(new WorkRecordModel)->saveAll($datas);
//领取完成,待领取清0;
$logData = [
'user_id' => $user->id.'',
'currency' => 'currency6',
'amount' => (0-$user->currency6).'',
'before' => $user->currency6.'',
'after' => '0',
'type' => \app\enum\BalanceType::CLAIM->value,
'created_at' => $time.'',
'memo' => ''
];
// 写入日志
BalanceLog::create($logData);
$user->currency6 = 0;
$user->save();
Db::commit();
} catch (\Exception $e) {
Db::rollback();
return $this->error($e->getMessage());
}
return $this->success(__('successful'));
}
/**
* 用户参与的问卷列表
* @Apidoc\Query("order_id", type="string", require=false, desc="订单号")
* @Apidoc\Query("step", type="string", require=false, desc="类型,progress,done")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function record(){
$limit = (int)input('limit',10);
$type = input('type','all');
$user_id = \support\Jwt\JwtToken::getCurrentId();
$step = input('step');
$order_id = input('order_id');
$model = WorkRecordModel::withJoin([
'questionnaire' => function($query) {
$query->field('title,category_id,country');
},
// 'product' => function($query) {
// $query->field('title');
// }
])->where('work_record.user_id',$user_id);
if($type && $type !='all'){
$model = $model->where('work_record.status', $type);
}
if($order_id){
$model = $model->where('work_record.order_id',$order_id);
}
if($step){
if($step=='done'){
$model = $model->where('work_record.status',\app\enum\ServerStatus::COMPLETE->value);
}
if($step=='progress'){
$model = $model->whereBetween('work_record.status',[1,\app\enum\ServerStatus::AUDITING->value,\app\enum\ServerStatus::SETTLEMENT->value]);
}
}
$list = $model->order('work_record.id desc')
->paginate($limit);
$list->each(function($item){
$item->questionnaire->country = Config('site.questionnaire_country')[$item->questionnaire->country];
$item->category = Db::name('category')->where('id',$item->questionnaire->category_id)->value('name');
return $item;
});
return $this->success(__('successful'),$list->toArray());
}
/**
* 开始任务
* @Apidoc\Method("POST")
* @Apidoc\Param("server_id", type="string", require=true, desc="产品ID")
*/
public function start()
{
$server_id = input('server_id');
$user = \support\Jwt::getUser();
if(!$server_id){
return $this->error(__('Incorrect parameter'));
}
if($server_id === 'all'){
$work_records = WorkRecordModel::where('user_id',$user->id)
->where('status',\app\enum\ServerStatus::WAITING->value)
->select();
}else{
$work_records = WorkRecordModel::where('user_id',$user->id)
->where('status',\app\enum\ServerStatus::WAITING->value)
->where('id',$server_id)->select();
if(count($work_records) === 0){
return $this->error(__('Server is not exist'));
}
}
/** @var WorkRecordModel $server */
foreach($work_records as $server){
$server->start();
}
return $this->success(__('successful'));
}
/**
* 用户参与的问卷详情
* @Apidoc\Method("POST")
* @Apidoc\Param("server_id", type="string", require=true, desc="产品ID")
*/
function progress(){
$server_id = (int)input('server_id',1);
$user = \support\Jwt::getUser();
/** @var WorkRecordModel $work_record */
$work_record = WorkRecordModel::with(['questionnaire'])->where('id',$server_id)->find();
if(!$work_record){
return $this->error(__('Server is not exist %sadds_f%',["%sadds_f%"=>'']));
}
$work_record->step_text = $work_record->getStep();
return $this->success(__('successful'),$work_record->toArray());
}
}
@@ -1,481 +0,0 @@
<?php
namespace app\api\controller;
use support\Request;
use support\think\Db;
use taoser\facade\Validate;
use app\model\Recharge as RechargeModel;
use app\model\User as UserModel;
use hg\apidoc\annotation as Apidoc;
/**
* 充值模块
*/
class RechargeController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = ['notify','watch_list','updateUserBalance'];
/**
* 列表
* @Apidoc\Method("POST")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list()
{
$limit = (int)input('limit',10);
$status = Input('status',null);
if(!is_null($status)){
$list = RechargeModel::where('user_id',\support\Jwt\JwtToken::getCurrentId())->where('status',$status)->order('id desc')->paginate($limit);
}else{
$list = RechargeModel::where('user_id',\support\Jwt\JwtToken::getCurrentId())->order('id desc')->paginate($limit);
}
return $this->success(__('successful'),$list->toArray());
}
/**
* 创建
* @Apidoc\Method("POST")
* @Apidoc\Param("amount", type="string", require=true, desc="金额")
* @Apidoc\Param("network", type="string", require=true, desc="网络")
*/
function create(){
return $this->create1();
}
public function create2()
{
$user = \support\Jwt::getUser();
$data = [
'user_id' => $user->id,
'amount' => input('amount',0),
'network' => input('network',''),
'status' => \app\enum\WithdrawlStatus::CREATED->value
];
//验证最小金额
if($data['amount'] < Config('site.recharge_minimum')){
return $this->error(__('Minimum recharge of %num%',[
'%num%' => Config('site.recharge_minimum')
]));
}
if (!$data['network'] || !in_array($data['network'],['BEP-20','TRC-20','wxpay','alipay','qqpay'])) {
return $this->error(__('Network is incorrect'));
}
if(in_array($data['network'],['BEP-20','TRC-20'])){
$ing_count = RechargeModel::where('user_id',$data['user_id'])->where('status',\app\enum\RechargeStatus::CREATED->value)->count('id');
if($ing_count >= 2){
return $this->error(__('You have reached the limit of uncompleted orders ( %max% ). Please complete the existing orders before trying to create another one.',[
'%max%' => 2
]));
}
Db::startTrans();
try {
/** @var RechargeModel $order */
$order = RechargeModel::create($data);
//$order->notify_url = Request()->domain().'/api/recharge/notify';
$order->notify_url = config('pay.notify_server').'/api/recharge/notify';
$order->out_trade_no = $order->id;
$order->env = 'product';
$order->appid = Config('pay.appid');
$order->created_at = time();
$postdata = $order->toArray();
unset($postdata['id']);
//转换成USDT
$postdata['amount'] = bcdiv($order->amount,Config('site.money_to_usdt_rate'),4);
//折扣
$postdata['amount'] = bcdiv($postdata['amount'],Config('site.usdt_recharge_discount'),4);
$res = post(Config('pay.server').'/recharge/create',$postdata);
\support\Log::alert("create res:".$res);
//cp($res);
$res = json_decode($res,true);
if($res['code'] === 0){
$order->address = $res['data']['address'];
}else{
Db::rollback();
return $this->error(__('Failed to create recharge order, please try again later'));
}
$order->allowField(['address'])->save();
Db::commit();
return $this->success(__('successful'),[
'order' => $order
]);
} catch (\Exception $e) {
Db::rollback();
\support\Log::alert("create error".$e->getMessage());
return $this->error(__('Failed to create recharge order, please try again later'));
}
}else{
$sign = aesencode(json_encode($data));
return $this->success(__('successful'),[
'order' => [
'id' => 0,
'network' => $data['network'],
'amount' => $data['amount'],
"url" => 'http://43.240.74.89:9595/pay/index?sign='.$sign
]
]);
}
}
public function create1()
{
$user = \support\Jwt::getUser();
$data = [
'user_id' => $user->id,
'amount' => input('amount',0),
'network' => input('network',''),
'status' => \app\enum\WithdrawlStatus::CREATED->value
];
//验证最小金额
if($data['amount'] < Config('site.recharge_minimum')){
return $this->error(__('Minimum recharge of %num%',[
'%num%' => Config('site.recharge_minimum')
]));
}
if (!$data['network'] || !in_array($data['network'],['BEP-20','TRC-20','wxpay','alipay','qqpay'])) {
return $this->error(__('Network is incorrect'));
}
if(in_array($data['network'],['BEP-20','TRC-20'])){
$ing_count = RechargeModel::where('user_id',$data['user_id'])->where('status',\app\enum\RechargeStatus::CREATED->value)->count('id');
if($ing_count >= 2){
return $this->error(__('You have reached the limit of uncompleted orders ( %max% ). Please complete the existing orders before trying to create another one.',[
'%max%' => 2
]));
}
}
Db::startTrans();
try {
/** @var RechargeModel $order */
$order = RechargeModel::create($data);
if(in_array($data['network'],['BEP-20','TRC-20'])){
//$order->notify_url = Request()->domain().'/api/recharge/notify';
$order->notify_url = config('pay.notify_server').'/api/recharge/notify';
$order->out_trade_no = $order->id;
$order->env = 'product';
$order->appid = Config('pay.appid');
$order->created_at = time();
$postdata = $order->toArray();
unset($postdata['id']);
//转换成USDT
$postdata['amount'] = bcdiv($order->amount,Config('site.money_to_usdt_rate'),4);
//折扣
$postdata['amount'] = bcmul($postdata['amount'],Config('site.usdt_recharge_discount'),4);
$res = post(Config('pay.server').'/recharge/create',$postdata);
\support\Log::alert("create res:".$res);
//cp($res);
$res = json_decode($res,true);
if($res['code'] === 0){
$order->address = $res['data']['address'];
}else{
Db::rollback();
return $this->error(__('Failed to create recharge order, please try again later'));
}
$order->allowField(['address'])->save();
}else{
$postdata = [
"pid" => '144604',
"type" => $order->network,
"out_trade_no" => $order->id,
"notify_url" => Config('pay.notify_server').'/api/recharge/notify_ok',
"return_url" => Config('pay.notify_server').'/api/recharge/notify_ok',
"name" => 'VIP会员',
"money" => $order->amount,
"device" => "mobile",
"clientip" => request()->getRealIp()
];
$postdata['money'] = bcdiv($postdata['money'] ,Config('site.rmb_recharge_discount'),2);
//\support\Log::alert("create postdata:".json_encode($postdata));
$this->getsign($postdata);
$res = post('https://pay.yf2.cn/mapi.php',$postdata);
\support\Log::alert("create res:".$res);
$res = json_decode($res,true);
if($res['code'] === 1){
if(isset($res['payurl'])){
$order->address = $res['qrcode'];
}elseif(isset($res['qrcode'])){
$order->address = $res['qrcode'];
}elseif(isset($res['urlscheme'])){
$order->address = $res['urlscheme'];
}
}else{
Db::rollback();
\support\Log::error(json_encode($res));
throw new \Exception($res['msg']);
}
$order->allowField(['address'])->save();
}
Db::commit();
return $this->success(__('successful'),[
'order' => $order
]);
} catch (\Exception $e) {
Db::rollback();
\support\Log::alert("create error".$e->getMessage());
return $this->error(__('Failed to create recharge order, please try again later'));
}
}
/**
* 更新
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="int", require=true, desc="ID")
* @Apidoc\Param("amount", type="string", require=true, desc="金额")
* @Apidoc\Param("network", type="string", require=true, desc="网络")
*/
function update(){
$id = input('id');
$amount = input('amount');
$network = input('network');
$data = [];
if(!$id){
return $this->error(__('Invalid parameters'));
}
/** @var RechargeModel $order */
$order = RechargeModel::where('id',$id)->find();
if(!$order){
return $this->error(__('Order not exsit'));
}
if($amount && $amount != $order->amount){
$data['amount'] = $amount;
//验证最小金额
if($data['amount'] < Config('site.recharge_minimum')){
return $this->error(__('Minimum recharge of %num%',[
'%num%' => Config('site.recharge_minimum')
]));
}
$data['amount'] = bcdiv($data['amount'],Config('site.money_to_usdt_rate'),4);
}
if($network && $network != $order->network){
$data['network'] = $network;
if (!$data['network'] || !in_array($data['network'],['BEP-20','TRC-20'])) {
return $this->error(__('Network is incorrect'));
}
}
if(empty($data)){
return $this->error(__('Invalid parameters'));
}
foreach($data as $field=>$value){
$order->$field = $value;
}
$data['out_trade_no'] = $order->id;
$data['action'] = 'update';
$data['appid'] = Config('pay.appid');
$res = post(Config('pay.server').'/recharge/create',$data);
//\support\Log::alert("update res:".$res);
$res = json_decode($res,true);
//cp($res);
if($res['code'] === 0){
if($order->address != $res['data']['address']){
$order->address = $res['data']['address'];
}
}else{
return $this->error(__('Failed to create recharge order, please try again later'));
}
$order->save();
return $this->success(__('successful'),[
'order' => $order
]);
}
/**
* 详情
* @Apidoc\Query("id", type="string", require=true, desc="ID")
*/
public function detail(){
$appid = input('id');
$vo = RechargeModel::where('id',$appid)->find();
if($vo) {
return $this->success(__('successful'),[
'order' => $vo
]);
}
return $this->error(__("Record does not exist"));
}
/**
* 转账成功异步通知
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
public function notify(){
$data = aesdecode(input('data'));
$data = json_decode($data,true);
/** @var RechargeModel $vo */
$vo = RechargeModel::where('id',$data['out_trade_no'])->find();
if($vo){
if($data['result'] == 'SUCCESS'){
if($vo->status != \app\enum\RechargeStatus::COMPLETE->value){
try{
$vo->status = \app\enum\RechargeStatus::COMPLETE->value;
$vo->txid = $data['txid'];
$vo->real_amount = isset($data['real_amount']) ? $data['real_amount'] : 0;
$vo->transfer_at = $data['transfer_at'] ?: time();
$vo->save();
//消除USDT和人民币的误差,因为USDT支付可能不是一模一样的金额
$money = bcmul($vo->amount ,Config('site.rmb_to_money_rate'));
UserModel::money($vo->user_id,$money,\app\enum\BalanceType::RECHARGE,$vo->id);
Hook('recharge.success',$vo);
}catch(\Exception $e){
log_write('充值回调失败:'.$e->getMessage());
log_write($data);
}
}
}else{
$vo->status = \app\enum\RechargeStatus::FAIL->value;
$vo->txid = $data['txid'];
$vo->reason = $data['reason'];
$vo->save();
}
}
return response("SUCCESS");
}
/**
* 人民币转账成功异步通知
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
public function notify_ok(){
$data = Input('post.');
$sign = get_pay_sign($data);
if($sign != $data['sign']){
return response("FAIL");
}
/** @var RechargeModel $vo */
$vo = RechargeModel::where('id',$data['out_trade_no'])->find();
if($vo && isset($data['trade_status'])){
if($data['trade_status'] === 'TRADE_SUCCESS'){
if($vo->status != \app\enum\RechargeStatus::COMPLETE->value){
try{
$vo->status = \app\enum\RechargeStatus::COMPLETE->value;
$vo->txid = isset($data['trade_no']) ? $data['trade_no'] : '';
$vo->from='pay.yf2.cn';
$vo->real_amount = isset($data['money']) ? $data['money'] : 0;
$vo->save();
$money = bcmul($vo->real_amount ,Config('site.rmb_to_money_rate'));
UserModel::money($vo->user_id,$money,\app\enum\BalanceType::RECHARGE,$vo->id);
Hook('recharge.success',$vo);
}catch(\Exception $e){
log_write('充值回调失败:'.$e->getMessage());
log_write($data);
return response("FAIL");
}
}
}
}
return response("SUCCESS");
}
/**
* 根据TXID更新用户余额,补单的功能
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
* @return \support\Response
*/
function update_balance_by_txid(){
$user_id = \support\Jwt\JwtToken::getCurrentId();
$data = [
'txid' => input('txid'),
'id' => input('id'),
'status' => \app\enum\WithdrawlStatus::CREATED->value
];
/** @var RechargeModel $vo */
$vo = RechargeModel::where('id',$data['id'])->find();
if($vo->user_id !=$user_id){
return $this->error(__('permission denied'));
}
//验证最小金额
if(!$data['txid']){
return $this->error(__('Invalid parameters'));
}
$res = get(Config('pay.server').'/Util/txid?txid='.$data['txid']);
$res = json_decode($res,true);
if($res['code'] !== 0){
return $this->error($res['msg']);
}
$vo->txid = $data['txid'];
$vo->result = $res['result'];
if($res['result'] == 'SUCCESS'){
$vo->from = $res['from'];
$vo->real_amount = $res['real_amount'];
$vo->pay_time = $res['timestamp'];
$vo->status = \app\enum\RechargeStatus::COMPLETE->value;
}else{
$vo->result = $res['from'];
$vo->reason = $res['real_amount'];
$vo->status = \app\enum\RechargeStatus::FAIL->value;
}
$vo->save();
return $this->success(__('successful'));
}
/**
* 根据监视上报数据更新用户余额
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
function updateUserBalance(Request $request){
$decimal_part = $request->post('decimal_part');
$data = [
'user_id' => 0 ,
'amount' => $request->post('amount'),
'network' => $request->post('chain'),
'address' => $request->post('to'),
'from' => $request->post('from'),
'txid' => $request->post('txid'),
'pay_time' => $request->post('pay_time'),
'created_at' => $request->post('pay_time'),
];
if($data['network'] == 'TRC-20'){
$data['user_id'] = UserModel::where('trc_recharge_address',$data['address'])->where('decimal_part',$decimal_part)->value('id');
}else{
$data['user_id'] = UserModel::where('bep_recharge_address',$data['address'])->where('decimal_part',$decimal_part)->value('id');
}
if(!$data['user_id']){
return $this->success(__('user not exist'));
}
$data['real_amount'] = $data['amount'];
//$data['pay_time'] = time();
$data['status'] = 2;
$vo = RechargeModel::where('txid',$data['txid'])->find();
if($vo){
return $this->success(__('exist'));
}
Db::startTrans();
try{
$idata = RechargeModel::create($data);
UserModel::score($data['user_id'],$data['amount'],\app\enum\BalanceType::RECHARGE,$idata['id'].'');
Db::commit();
Hook('recharge.success',$idata);
return $this->success(__('successful'));
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
}
/**
* 监视列表
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
function watch_list(){
//$bep = UserModel::distinct(true)->column('LOWER(bep_recharge_address)');
//$trc = UserModel::distinct(true)->column('LOWER(trc_recharge_address)');
$bep = UserModel::distinct(true)->column('bep_recharge_address');
$trc = UserModel::distinct(true)->column('trc_recharge_address');
return $this->success(__('successful'),[
'BEP-20'=>$bep,
'TRC-20'=>$trc,
]);
}
protected function getsign(&$data, $key = "3E7551E3707DFB6B6E8A6E83B01FF437") {
return get_pay_sign($data,$key);
}
}
@@ -1,122 +0,0 @@
<?php
namespace app\api\controller;
use support\Request;
use support\Response;
use support\think\Db;
use hg\apidoc\annotation as Apidoc;
/**
* 用户角色
*/
class RoleController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = ['recent'];
/**
* @Apidoc\Title("当前角色信息")
* @Apidoc\Method("GET")
*/
function detail(){
$user = \support\Jwt::getUser();
$data = \app\model\UserRole::where('id',$user->role_id)->field('name,id,price')->find();
return $this->success(__('successful'),$data);
}
/**
* @Apidoc\Title("列表")
* @Apidoc\Method("GET")
* @Apidoc\Param("page", type="int",require=false, desc="页码")
* @Apidoc\Param("limit", type="int",require=false, desc="分页大小")
*/
public function list(Request $request){
$user = \support\Jwt::getUser();
$limit = $request->get('limit',10);
$page = $request->get('page',1);
$kw = $request->get('kw');
$model = \app\model\UserRole::where('id','>',0)->field('id,name,price')
->order('id asc');
if($limit == 'all' || $limit >= 999999){
$result = $model->select();
$result= \think\Paginator::make($result, 99999999999, 1, count($result));
}else{
$result = $model->paginate($limit);
}
return $this->success(__('successful'),$result);
}
/**
* @Apidoc\Title("最近购买列表")
* @Apidoc\Method("GET")
*/
public function recent(Request $request){
$list = (new \app\model\BalanceLog)->setSuffix('_money')->where('type',\app\enum\BalanceType::PURCHASE_ROLE->value)
->order('created_at','desc')->limit(0,10)->field('user_id,created_at,memo')->select();
$list = $list->toArray();
$data = [];
$role_list = \app\model\UserRole::where('id', '>',1)
->column('name','id');
foreach($list as $v){
$data[] = [
'username' => \app\model\User::where('id',$v['user_id'])->value('username'),
'created_at' => $v['created_at'],
'v' => $v,
//'role' => $role_list[str_replace('购买用户组:','',$v['memo'])]
'role' => 'K'.str_replace('购买用户组:','',$v['memo'])
];
}
return $this->success(__('successful'),$data);
}
/**
* @Apidoc\Title("购买")
* @Apidoc\Method("POST")
* @Apidoc\Param("role_id", type="string",require=true, desc="要购买的角色ID")
* @Apidoc\Param("trade_password", type="string",require=true, desc="交易密码")
*/
function buy(Request $request): Response{
$user = \support\Jwt::getUser();
$role_id = (int)$request->post('role_id');
if($user->role_id > $role_id){
return $this->fail(__('Your level is too high to purchase this character'));
}
if(abs($role_id - $user->role_id) !== 1 && $role_id !==6){
return $this->fail(__('Your level is too high to purchase this character'));
}
$role = \app\model\UserRole::where('id',$role_id)->find();
if(!$role){
return $this->fail(__('Role does not exist'));
}
if($role->price <=0){
return $this->fail(__('This character group is not allowed to be sold'));
}
if($user->money < $role->price){
return $this->fail(__('Insufficient balance'));
}
\support\Jwt::verify_trade_password($request->post('trade_password'));
$amount = $role->price;
$power = bcmul($amount,config('site.suanli_rate'),4);
\app\model\User::where('id',$user->id)->save(['role_id'=>$role_id]);
\app\model\User::money($user->id,-$amount,\app\enum\BalanceType::PURCHASE_ROLE,'购买用户组:'.$role_id);
\app\model\User::score($user->id,$power,\app\enum\BalanceType::POWER_ADD,'购买用户组:'.$role_id);
cache_add('user_power_total_'.$user->id,$power);
Hook('user.roleup', $user);
$data = [
'role_id' => $role_id,
'user_id' => $user->id,
'parent_id' => $user->parent_id,
'amount' => $amount,
];
Hook('role.buy', $data);
return $this->success(__('successful'));
}
}
@@ -1,293 +0,0 @@
<?php
namespace app\api\controller;
use support\Request;
use support\Response;
use app\model\UserSignin as UserSigninModel;
use app\model\UserXuanchuan as UserXuanchuanModel;
use support\Jwt\JwtToken;
use Shopwwi\WebmanFilesystem\FilesystemFactory;
use Shopwwi\WebmanFilesystem\Facade\Storage;
use hg\apidoc\annotation as Apidoc;
/**
* 签到模块
*/
class SigninController extends BaseController
{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
/**
* 列表
* @Apidoc\Query("status", type="int", require=true, desc="状态")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list(){
$limit = (int)input('limit',10);
$status = (int)input('status');
$model = UserXuanchuanModel::where('id','>',0);
if($status && $status!='all'){
$model = $model->where('status',$status);
}
$list = $model->order('id','desc')->paginate($limit);
return $this->success(__('successful'),$list);
}
/**
* @Apidoc\Title("用户签到")
* @Apidoc\Method("GET")
*/
public function info(Request $request)
{
$user_id = JwtToken::getCurrentId();
if (!$user_id) {
return $this->error(__('Please login first'));
}
/** @var UserSigninModel $last */
$last = UserSigninModel::where('user_id', $user_id)->order('id','desc')->find();
return $this->success(__('successful'),[
'continuous_days' => $last->continuous_days,
'last_day' => $last->sign_date,
'signed' => $last->sign_date == date('Y-m-d'),
'invite_complete' => cache('invite_'.$user_id.'_'.date('Ymd')),
'pyq_complete' => UserXuanchuanModel::where('user_id',$user_id)->where('type','pyq')->whereTime('created_at','today')->count() > 0,
'group_complete' => UserXuanchuanModel::where('user_id',$user_id)->where('type','group_complete')->whereTime('created_at','today')->count() > 0,
'signinList' => [20,20,20,20,20,20,100]
]);
}
/**
* @Apidoc\Title("用户签到")
* @Apidoc\Method("GET")
*/
public function sign(Request $request)
{
$user = JwtToken::getUser();
if (!$user->realname_verify) {
return $this->error(__('Please complete real-name verification first'));
}
$user_id = $user->id;
$today = date('Y-m-d');
// 检查今天是否已签到
if (UserSigninModel::where('user_id', $user_id)->where('sign_date', $today)->find()) {
return $this->error(__('今日已签到'));
}
// 检查昨天是否签到
$continuous_days = UserSigninModel::where('user_id', $user_id)->count('id');
$continuous_days = $continuous_days ? ($continuous_days + 1) : 1;
// 奖励规则(可自定义/读取配置/数据库)
$reward = $this->getReward($continuous_days);
// 写入签到记录
UserSigninModel::create([
'user_id' => $user_id,
'sign_date' => $today,
'reward' => $reward,
'continuous_days' => $continuous_days,
]);
// 发放奖励(如积分、余额等)
\app\model\User::currency1($user_id, $reward, \app\enum\BalanceType::SIGNIN);
return $this->success(__('successful'),[
'reward' => $reward,
'continuous_days' => $continuous_days
]);
}
/**
* @Apidoc\Title("查询签到状态")
* @Apidoc\Method("GET")
*/
public function status(Request $request)
{
$user_id = JwtToken::getCurrentId();
if (!$user_id) {
return $this->error(__('Please login first'));
}
$today = date('Y-m-d');
$record = UserSigninModel::where('user_id', $user_id)->where('sign_date', $today)->find();
$continuous_days = UserSigninModel::where('user_id', $user_id)->order('id','desc')->value('continuous_days');
return $this->success(__('successful'),[
'signed' => !!$record,
'continuous_days' => $continuous_days ?: 0,
]);
}
/**
* @Apidoc\Title("查询签到记录")
* @Apidoc\Method("GET")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function records(Request $request)
{
$user_id = JwtToken::getCurrentId();
if (!$user_id) {
return $this->error(__('Please login first'));
}
$limit = (int)$request->get('limit', 30);
$list = UserSigninModel::where('user_id', $user_id)
->order('id','desc')
->limit($limit)
->select();
return $this->success(__('successful'),$list);
}
/**
* @Apidoc\Title("发布朋友圈")
* @Apidoc\Method("POST")
* @Apidoc\Param("files", type="string",require=true, desc="文件列表")
*/
public function pyq(Request $request)
{
$user = JwtToken::getUser();
if (!$user->realname_verify) {
return $this->error(__('Please complete real-name verification first'));
}
$user_id = $user->id;
$files = Input('files');
if(count($files) != 2) {
return $this->error(__('请上传2张图片'));
}
if(UserXuanchuanModel::where('user_id',$user_id)->where('type','pyq')->whereTime('created_at','today')->count() > 0) {
return $this->error(__('请明日再来'));
}
UserXuanchuanModel::create([
'user_id' => $user_id,
'files' => implode(',',$files),
'type' => 'pyq'
]);
return $this->success(__('successful'));
}
/**
* @Apidoc\Title("发布微信群")
* @Apidoc\Method("POST")
* @Apidoc\Param("files", type="string",require=true, desc="文件")
*/
public function wx(Request $request)
{
$user = JwtToken::getUser();
if (!$user->realname_verify) {
return $this->error(__('Please complete real-name verification first'));
}
$user_id = $user->id;
$files = Input('files');
if(count($files) != 1) {
return $this->error(__('请上传1张图片'));
}
if(UserXuanchuanModel::where('user_id',$user_id)->where('type','group')->whereTime('created_at','today')->count() > 0) {
return $this->error(__('请每日再来'));
}
UserXuanchuanModel::create([
'user_id' => $user_id,
'files' => implode(',',$files),
'type' => 'group'
]);
return $this->success(__('successful'));
}
/**
* @Apidoc\Title("补签")
* @Apidoc\Method("POST")
* @Apidoc\Param("date", type="string",require=true, desc="补签日期")
*/
public function makeUp(Request $request)
{
$user_id = JwtToken::getCurrentId();
if (!$user_id) {
return $this->error(__('Please login first'));
}
$date = $request->post('date');
$today = date('Y-m-d');
if (!$date || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
return $this->error(__('日期格式错误'));
}
// 补签日期不能大于今天
if ($date > $today) {
return $this->error(__('补签日期不能大于今天'));
}
// 检查是否已签到
if (UserSigninModel::where('user_id', $user_id)->where('sign_date', $date)->find()) {
return $this->error(__('该日已签到'));
}
// 补签消耗积分或奖励减半(此处以奖励减半为例)
$yesterday = date('Y-m-d', strtotime($date . ' -1 day'));
/** @var UserSigninModel $last */
$last = UserSigninModel::where('user_id', $user_id)->where('sign_date', $yesterday)->find();
$continuous_days = $last ? ($last->continuous_days + 1) : 1;
$reward = floor($this->getReward($continuous_days) / 2); // 奖励减半
UserSigninModel::create([
'user_id' => $user_id,
'sign_date' => $date,
'reward' => $reward,
'continuous_days' => $continuous_days,
]);
\app\model\User::currency1($user_id, $reward, \app\enum\BalanceType::SIGNIN);
return $this->success(__('successful'),['reward' => $reward, 'continuous_days' => $continuous_days]);
}
/**
* @Apidoc\Title("签到统计报表")
* @Apidoc\Method("GET")
*/
public function report(Request $request)
{
$user_id = JwtToken::getCurrentId();
if (!$user_id) {
return $this->error(__('Please login first'));
}
$total = UserSigninModel::where('user_id', $user_id)->count();
$max_continuous = UserSigninModel::where('user_id', $user_id)->max('continuous_days');
$month = date('Y-m');
$month_count = UserSigninModel::where('user_id', $user_id)
->whereLike('sign_date', "$month%")
->count();
return $this->success(__('successful'),[
'total' => $total,
'max_continuous' => $max_continuous,
'month_count' => $month_count,
]);
}
// 奖励规则,可自定义
protected function getReward($continuous_days)
{
$rewards = [20,20,20,20,20,20,100];
$continuous_days = $continuous_days - 1;
$continuous_days = $continuous_days % 7;
return $rewards[$continuous_days];
}
/**
* @Apidoc\Title("上传")
* @Apidoc\Method("POST")
* @Apidoc\Param("file", type="string",require=true, desc="文件")
*/
function upload(Request $request)
{
//多文件上传
$files = $request->file();
try {
$result = Storage::adapter('public')
->path('upload/files')
->size(1024*1024*50)
->extYes(['image/jpeg','image/png'])
->uploads($files,0,1024*1024*20,false);
return $this->success(__('successful'),$result);
}catch (\Exception $e){
return $this->error($e->getMessage());
}
}
}
-207
View File
@@ -1,207 +0,0 @@
<?php
namespace app\api\controller;
use app\model\User as UserModel;
use app\model\UserTeam as UserTeamModel;
use app\model\WorkRecord as WorkRecordModel;
use support\Request;
use support\think\Db;
use hg\apidoc\annotation as Apidoc;
/**
* 用户团队
*/
class TeamController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
/**
* @Apidoc\Title("团队概览")
* @Apidoc\Method("GET")
* @Apidoc\Param("username", type="string",require=true, desc="用户名")
* @Apidoc\Param("nickname", type="string",require=true, desc="密码")
*/
public function index(Request $request){
$user = \support\Jwt::getUserinfo();
$user_id = $user['id'];
$user= Hook('user.profile',$user);
$team_ids = UserTeamModel::where('ancestor_id',$user_id)->where('depth','>',0)->column('descendant_id');
$result=[
'total_count' => count($team_ids),//团队总人数
'direct_total' => cache('team_direct_total_'.$user_id)??0,//直属团队人数
'recharge_total' => cache('team_recharge_total_'.$user_id)??0,
'withdrawl_total' => cache('team_withdrawl_total_'.$user_id)??0,
'income_total' => cache('team_income_total_'.$user_id)??0,
'today_income_total' => cache('user_today_income_total_'.$user_id)??0,
'promotion_income_total' => cache('user_promotion_income_total_'.$user_id)??0,
'consume_total' => Db::name('user_extend')->where('user_id',$user_id)->value('sales'),//cache('team_consume_total_'.$user_id)??0,//团队总业绩
'user_sales_reward' => cache('user_sales_reward_'.$user_id)??0,//销售奖
'user_output_reward' => cache('user_output_reward_'.$user_id)??0,//产值奖
'user_withdrawl_reward' => cache('user_withdrawl_reward'.$user_id)??0,//提现奖
'user' => $user[0],
];
return $this->success(__('successful'),$result);
}
/**
* @Apidoc\Title("团队列表")
* @Apidoc\Method("GET")
* @Apidoc\Param("page", type="int",require=false, desc="页码")
* @Apidoc\Param("limit", type="int",require=false, desc="分页大小")
*/
public function list(Request $request){
$user = \support\Jwt::getUser();
$limit = $request->get('limit',10);
$page = $request->get('page',1);
$kw = $request->get('kw');
// 假设 $user_id 是要查询的用户 ID
// $user = User::find($user['id']); // 查询用户对象
// // 获取该用户的下属团队
// $teamMembers = $user->team()
// ->where('status', 1) // 只查询有效的下属
// ->with('user') // 联合查询 User 模型,获取下属的详细信息
// ->order('depth') // 按照层级深度排序
// ->select();
// foreach ($teamMembers as $member) {
// echo '下属用户ID: ' . $member->descendant_id . ',层级深度: ' . $member->depth . ',状态: ' . $member->status . ',用户名: ' . $member->user->name . ',邮箱: ' . $member->user->email . "\n";
// }
// return $this->success(__('successful'),$result);
// $model = Db::name('user_team')
// ->alias('ut')
// ->join('user wu', 'ut.descendant_id = wu.id')
// ->where('ut.ancestor_id', $user['id'])
// ->where('ut.depth', '<=', 3) // 限制三级内
// ->field('wu.id, wu.username, wu.group, ut.depth')
// ->order('ut.depth ASC, wu.username ASC');
// if($limit == 'all' || $limit >= 999999){
// $result = $model->select();
// }else{
// // 分页处理
// $result = $model->page($page, $limit)->select();
// $total = $model->count(); // 获取总记录数
// $result->each(function ($item) {
// //cache_add('user_recharge_total_'.$item['id'],1);
// //cache_add('user_withdrawl_total_'.$item['id'],1);
// //cache_add('user_income_total_'.$item['id'],1);
// $item['avatar'] = cdnurl($item['avatar'] ?: '/storage/avatar/default.png');
// $item['recharge_total'] = cache('user_recharge_total_'.$item['id']);
// $item['withdrawl_total'] = cache('user_withdrawl_total_'.$item['id']);
// $item['income_total'] = cache('user_income_total_'.$item['id']);
// $item['created_at'] = date('Y-m-d H:i:s', $item['created_at']);
// return $item;
// });
// $result = [
// 'data' => $result,
// 'total' => $total,
// 'current_page' => $page,
// 'last_page' => ceil($total / $limit),
// 'per_page' => $limit,
// ];
// }
$user_id = \support\Jwt\JwtToken::getCurrentId();
$model = UserModel::alias('u')
->where('parent_id',$user_id)
->join('user_extend ue', 'u.id = ue.user_id')
->where('u.parent_id', $user['id'])
//->where('ue.active', 1)
->field('u.id, u.username,u.money,u.score,u.role_id, u.group,u.avatar, u.created_at')
->order('u.created_at desc');
if($kw){
$model = $model->whereLike("u.username",'%'.$kw.'%');
}
if($limit == 'all' || $limit >= 999999){
$result = $model->select();
// $result = [
// 'data' => $result,
// 'total' => count($result),
// 'current_page' => 1,
// 'last_page' => 1,
// 'per_page' => count($result),
// ];
$result= \think\Paginator::make($result, 99999999999, 1, count($result));
}else{
$result = $model->paginate($limit);
}
$role_arr = [
'0' => __('普通用户'),
'1' => __('V1'),
'2' => __('V2'),
'3' => __('V3'),
'4' => __('V4'),
'5' => __('V5'),
];
$result = $result->toArray();
foreach($result['data'] as $k=>$item){
$result['data'][$k]['avatar'] = cdnurl($item['avatar'] ?: '/storage/avatar/default.png');
$result['data'][$k]['recharge_total'] = cache('user_recharge_total_'.$item['id'])??0;
$result['data'][$k]['withdrawl_total'] = cache('user_withdrawl_total_'.$item['id'])??0;
$result['data'][$k]['withdrawl_reward'] = cache('user_withdrawl_reward_'.$item['id'])??0;
$result['data'][$k]['income_total'] = cache('user_income_total_'.$item['id'])??0;
$result['data'][$k]['consume_total'] = cache('user_consume_total_'.$item['id'])??0;
$result['data'][$k]['play_count'] = cache('user_play_count_'.$item['id'])??0;
//$result['data'][$k]['created_at'] = date('Y-m-d H:i:s', $item['created_at']);
$result['data'][$k]['total_count'] = UserTeamModel::where('ancestor_id',$item['id'])->where('status',1)->where('depth','>',0)->count('descendant_id');
$result['data'][$k]['direct_total'] = cache('team_direct_total_'.$item['id'])??0;
$performance = get_performance($item['id']);
$result['data'][$k]['performance_large'] = $performance[0];
$result['data'][$k]['performance_small'] = $performance[1];
$result['data'][$k]['role'] = isset($role_arr[$item['role_id']]) ? $role_arr[$item['role_id']] : __('普通用户');
$result['data'][$k]['level'] = get_user_level($item['id'],$item['performance_small']);
$result['data'][$k]['questionnaire_count'] = WorkRecordModel::where('user_id',$item['id'])->count('id');
$result['data'][$k]['id'] = idEncode($item['id']);
//return $item;
}
return $this->success(__('successful'),$result);
}
/**
* @Apidoc\Title("改变用户等级")
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string",require=false, desc="ID")
* @Apidoc\Param("level", type="int",require=false, desc="等级")
*/
function changelevel(Request $request){
$user = \support\Jwt::getUser();
$id = $request->post('id');
$level = $request->post('level');
$id = idDecode($id);
if(!$id || !$level){
return $this->error(__('Invalid parameters'));
}
$child_user = UserModel::find($id);
if(!$child_user){
return $this->error(__('Invalid user'));
}
if($child_user->parent_id!=$user->id){
return $this->error(__('Access denied'));
}
if($user->role_id <= $level){
return $this->error(__('It cannot be lower than the user\'s current level'));
}
if($child_user->role_id >= $level){
return $this->error(__('It cannot be lower than the user\'s current level'));
}
$child_user->role_id = $level;
$child_user->save();
return $this->success(__('successful'));
}
}
-181
View File
@@ -1,181 +0,0 @@
<?php
namespace app\api\controller;
use Shopwwi\WebmanFilesystem\FilesystemFactory;
use Shopwwi\WebmanFilesystem\Facade\Storage;
use app\model\User as UserModel;
use app\model\Realname as RealnameModel;
use support\Request;
use taoser\facade\Validate;
use support\think\Db;
use hg\apidoc\annotation as Apidoc;
/**
* 用户相关
*/
class UserController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
/**
* @Apidoc\Title("个人资料")
* @Apidoc\Method("GET")
* @Apidoc\Desc("GET为获取用户信息,POST为修改数据")
* @Apidoc\Param("nickname", type="string",require=true, desc="昵称")
*/
public function profile(){
$data = \support\Jwt::getUser();
if(Request()->method() == 'POST'){
$nickname = input('nickname');
//$username = input('username');
//$password = input('password');
if(!$nickname){
return $this->error('Invalid nickname');
}
\support\Jwt::getUser()->save([
'nickname' => $nickname,
]);
return $this->success(__('successful'));
}
$data= Hook('user.profile',$data);
return $this->success(__('successful'),$data[0]);
}
/**
* @Apidoc\Title("修改密码")
* @Apidoc\Method("POST")
* @Apidoc\Param("password", type="string",require=true, desc="旧密码")
* @Apidoc\Param("newpassword", type="string",require=true, desc="新密码")
* @Apidoc\Param("renewpassword", type="string",require=true, desc="新密码")
*/
public function change_password(){
$password = input('password');
$newpassword = input('newpassword');
$renewpassword = input('renewpassword');
if (!$password || !$newpassword || !$renewpassword) {
return $this->error(__('Invalid parameters'));
}
if ($newpassword !== $renewpassword) {
return $this->error(__('Invalid parameters'));
}
try{
\support\Jwt::changepwd($newpassword,$password);
return $this->success(__('Reset password successful'));
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 修改交易密码
* @Apidoc\Method("POST")
* @Apidoc\Param("password", type="string",require=true, desc="旧密码(新设时可用为空)")
* @Apidoc\Param("newpassword", type="string",require=true, desc="新密码")
* @Apidoc\Param("renewpassword", type="string",require=true, desc="新密码")
*/
public function change_trade_password(){
$password = input('password');
$newpassword = input('newpassword');
$renewpassword = input('renewpassword');
if (!$newpassword || !$renewpassword || $newpassword !== $renewpassword) {
return $this->error(__('Invalid parameters'));
}
try{
\support\Jwt::change_trade_pwd($newpassword,$password);
return $this->success(__('Reset trade password successful'));
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 根据关键字查询用户列表
* @Apidoc\Method("POST")
* @Apidoc\Param("kw", type="string",require=true, desc="关键字")
*/
function getuserlist(){
$kw = Input('kw');
$user_id = \support\Jwt\JwtToken::getCurrentId();
$list = [];
if($kw){
//$list = User::where('id','<>',\support\Jwt\JwtToken::getCurrentId())->whereLike('nickname|username|email','%'.$kw.'%')->limit(0,10)->order('id asc')->field('id,username')->select();
//$list = User::where('id','<>',\support\Jwt\JwtToken::getCurrentId())->whereLike('username','%'.$kw.'%')->limit(0,10)->order('id asc')->field('id,username,username as name')->select();
$list = UserModel::whereLike('username','%'.$kw.'%')->where('id','<>',$user_id)->limit(0,10)->order('id asc')->field('id,username,username as name')->select();
// foreach($list as $k=>$v){
// }
}
return $this->success(__('successful'),$list);
}
/**
* 头像上传
* @Apidoc\Method("POST")
* @Apidoc\Param("file", type="File", require=true, desc="文件")
*/
public function avatar(Request $request)
{
//单文件上传
$file = $request->file('file0');
try {
$result = Storage::adapter('public')->path('upload/avatar')->size(1024*1024*5)->extYes(['image/jpeg','image/png'])->processUpload($file,function ($image){
$image->resize(200,200);
return $image;
},true);
\support\Jwt::getUser()->save([
'avatar' => '/'.$result->file_name,
]);
//$result->ss = cdnurl($result->url);
//P($result);
return $this->success(__('successful'),$result);
}catch (\Exception $e){
return $this->error($e->getMessage());
}
}
function realname(Request $request){
/**
* @var UserModel $user
*/
$user = \support\Jwt::getUser();
if($request->method() == 'POST'){
$data = [
'realname' => Input('realname'),
'idcard' => Input('idcard'),
'user_id' => $user->id,
];
log_alert($data);
if(!$data['realname'] || !$data['idcard']){
return $this->error(__('Incoret param'));
}
if($user->realname_verify == 1){
return $this->error(__('You have verified'));
}
if(RealnameModel::where('idcard',$data['idcard'])->where('user_id','<>',$user->id)->count()){
return $this->error(__('ID card already exists'));
}
Db::startTrans();
try {
RealnameModel::create($data);
$user->realname_verify = 1;
$user->save();
if($user->parent_id && cache('invite_'.$user->parent_id.'_'.date('Ymd')) < 1){
\app\model\User::currency1($user->parent_id,40,\app\enum\BalanceType::INVITE_NEW_USER);
cache('invite_'.$user->parent_id.'_'.date('Ymd'),1);
}
Db::commit();
return $this->success('ok',$user);
} catch (\Exception $e) {
Db::rollback();
return $this->error(__($e->getMessage()));
}
}else{
$user->realname = RealnameModel::where('user_id',$user->id)->find();
return $this->success('ok',$user);
}
}
}
@@ -1,163 +0,0 @@
<?php
namespace app\api\controller;
use app\model\User as UserModel;
use hg\apidoc\annotation as Apidoc;
/**
* 验证接口
*/
class ValidateController extends BaseController
{
public $noNeedLogin = '*';
/**
* 检测邮箱是否可用
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱")
* @Apidoc\Param("id", type="string",require=true, desc="排除会员ID")
*/
public function check_email_available()
{
$email = input('email');
$id = (int)input('id');
$count = UserModel::where('email', '=', $email)->where('id', '<>', $id)->count();
if ($count > 0) {
return $this->error(__('The mailbox is already occupied'));
}
return $this->success(__('successful'));
}
/**
* 检测用户名
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("username", type="string",require=true, desc="用户名")
* @Apidoc\Param("id", type="string",require=true, desc="排除会员ID")
*/
public function check_username_available()
{
$username = input('username');
$id = (int)input('id');
$count = UserModel::where('username', '=', $username)->where('id', '<>', $id)->count();
if ($count > 0) {
return $this->error(__('Username is already taken'));
}
return $this->success(__('successful'));
}
/**
* 检测昵称
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("nickname", type="string",require=true, desc="昵称")
* @Apidoc\Param("id", type="string",require=true, desc="排除会员ID")
*/
public function check_nickname_available()
{
$nickname = input('nickname');
$id = (int)input('id');
$count = UserModel::where('nickname', '=', $nickname)->where('id', '<>', $id)->count();
if ($count > 0) {
return $this->error(__('Nickname is already taken'));
}
return $this->success(__('successful'));
}
/**
* 检测手机
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("mobile", type="string",require=true, desc="手机号")
* @Apidoc\Param("id", type="string",require=true, desc="排除会员ID")
*/
public function check_mobile_available()
{
$mobile = input('mobile');
$id = (int)input('id');
$count = UserModel::where('mobile', '=', $mobile)->where('id', '<>', $id)->count();
if ($count > 0) {
return $this->error(__('Phone Number is already taken'));
}
return $this->success(__('successful'));
}
/**
* 检测手机是否存在
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("mobile", type="string",require=true, desc="手机号")
*/
public function check_mobile_exist()
{
$mobile = input('mobile');
$count = UserModel::where('mobile', '=', $mobile)->count();
if (!$count) {
return $this->error(__('Mobile number does not exist'));
}
return $this->success(__('successful'));
}
/**
* 检测邮箱是否存在
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱")
*/
public function check_email_exist()
{
$email = input('email');
$count = UserModel::where('email', '=', $email)->count();
if (!$count) {
return $this->error(__('Email does not exist'));
}
return $this->success(__('successful'));
}
/**
* 检测手机验证码(弃用)
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("mobile", type="string",require=true, desc="手机号")
* @Apidoc\Param("code", type="string",require=true, desc="验证码")
* @Apidoc\Param("event", type="string",require=true, desc="事件")
*/
protected function check_sms_correct()
{
$mobile = input('mobile');
$captcha = input('captcha');
$event = input('event');
// if (!\app\common\library\Sms::check($mobile, $captcha, $event)) {
// $this->error(__('Incorrect verification code'));
// }
return $this->success(__('successful'));
}
/**
* 检测邮箱验证码
*
* @Apidoc\Method ("POST")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱")
* @Apidoc\Param("code", type="string",require=true, desc="验证码")
* @Apidoc\Param("event", type="string",require=true, desc="事件")
*/
public function check_ems_correct()
{
$email = input('email');
$captcha = input('code');
$event = input('event');
$cache_key = 'captcha_'.$event.'_'.$email;
$list = cache($cache_key);
$list = $list?:[];
if(!isset($list[$captcha])){
return $this->error(__('Incorrect verification code'));
}
if($list[$captcha]+5*60 >= time()){
unset($list[$captcha]);
cache($cache_key,$list);
return $this->error(__('Verification code has expired'));
}
return $this->success(__('successful'));
}
}
@@ -1,227 +0,0 @@
<?php
namespace app\api\controller;
use app\model\User as UserModel;
use support\Request;
use app\model\Cdkey as CdkeyModel;
use taoser\facade\Validate;
use support\think\Db;
use hg\apidoc\annotation as Apidoc;
/**
* 钱包接口
*/
class WalletController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
/**
* 用户调研币兑换问卷指标
* @Apidoc\Method("POST")
* @Apidoc\Param("currency", type="string",require=true, desc="货币money_to_score")
* @Apidoc\Param("sendAmount", type="string",require=true, desc="调研币兑换数量")
* @Apidoc\Param("receiveAmount", type="string",require=true, desc="问卷指标兑换数量")
* @Apidoc\Param("trade_password", type="string",require=true, desc="交易密码")
* @Apidoc\Param("code", type="string",require=true, desc="图形验证码(event=exchange)")
*/
public function exchange(){
//return $this->error(__('The system is under maintenance, please wait...'));
$user = \support\Jwt\JwtToken::getUser();
// if(Config('site.trade_password_type') == 'email'){
// captcha_verfiy('email','exchange',$user['username']);
// }else{
// $trade_password = input('trade_password');
// \support\Jwt::verify_trade_password($trade_password);
// }
$currency_pair = input('currency');
$currencys = explode('_to_', $currency_pair);
$from_currency = $currencys[0];
$to_currency = $currencys[1];
if(!$from_currency || !$to_currency){
return $this->error(__('Invalid parameters'));
}
$sendAmount = (float)input('sendAmount');
$receiveAmount = (float)input('receiveAmount');
$rate = Config('site.'.$currency_pair.'_rate');
if(!$sendAmount || !$receiveAmount || !$rate){
return $this->error(__('Invalid parameters'));
}
$_receiveAmount = intval($sendAmount / $rate);
$_sendAmount = $_receiveAmount * $rate;
if($sendAmount > $user->$from_currency || $receiveAmount <= 0){
return $this->error(__('Invalid parameters').$sendAmount .'<' .$user->$from_currency .'||'. $receiveAmount);
}
Db::startTrans();
try{
UserModel::$from_currency($user->id,-$_sendAmount,\app\enum\BalanceType::EXCHANGE);
UserModel::$to_currency($user->id,$_receiveAmount,\app\enum\BalanceType::EXCHANGE);
Db::commit();
return $this->success(__('Exchange successful'));
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
}
/**
* 用户间score转账
* @Apidoc\Method("POST")
* @Apidoc\Param("username", type="string",require=true, desc="收款用户/用户ID")
* @Apidoc\Param("amount", type="string",require=true, desc="金额")
* @Apidoc\Param("trade_password", type="string",require=true, desc="交易密码")
* @Apidoc\Param("code", type="string",require=true, desc="图形验证码(event=transfer)")
*/
public function transfer(){
//return $this->error(__('The system is under maintenance, please wait...'));
$user = \support\Jwt::getUser();
$username = input('username');
if(!$username){
return $this->error(__('User is incorrect'));
}
/** @var UserModel $to_user */
if(str_contains($username,'@')){
$to_user = UserModel::where('username',$username)->find();
}else{
$to_user_id = idDecode($username);
$to_user = UserModel::where('id',$to_user_id)->find();
}
if(!$to_user){
return $this->error(__('User is incorrect'));
}
if(Config('site.trade_password_type') == 'email'){
//captcha_verfiy('email','transfer',$to_user['username']);
}else{
$trade_password = input('trade_password');
\support\Jwt::verify_trade_password($trade_password);
}
$amount = (float)input('amount');
if($amount <= 0){
return $this->error(__('Invalid parameters'));
}
if($user->score < $amount){
return $this->error(__('Insufficient balance'));
}
Db::startTrans();
try{
UserModel::score($user->id,-$amount,\app\enum\BalanceType::TRANSFER,$to_user->id);
UserModel::score($to_user->id,$amount,\app\enum\BalanceType::TRANSFER,$user->id);
Db::commit();
return $this->success(__('Transfer successful'));
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
}
/**
* 根据关键字查询用户列表
* @Apidoc\Method("POST")
* @Apidoc\Param("kw", type="string",require=true, desc="关键字")
*/
function getuserlist(){
$kw = Input('kw');
$user_id = \support\Jwt\JwtToken::getCurrentId();
$list = [];
if($kw){
//$list = User::where('id','<>',\support\Jwt\JwtToken::getCurrentId())->whereLike('nickname|username|email','%'.$kw.'%')->limit(0,10)->order('id asc')->field('id,username')->select();
//$list = User::where('id','<>',\support\Jwt\JwtToken::getCurrentId())->whereLike('username','%'.$kw.'%')->limit(0,10)->order('id asc')->field('id,username,username as name')->select();
$list = UserModel::whereLike('username','%'.$kw.'%')->where('id','<>',$user_id)->limit(0,10)->order('id asc')->field('id,username,username as name')->select();
// foreach($list as $k=>$v){
// }
}
return $this->success(__('successful'),$list);
}
/**
* 本地cdkey兑换
* @Apidoc\Method("POST")
* @Apidoc\Param("cdkey", type="string",require=true, desc="cdkey")
* @Apidoc\Param("trade_password", type="string",require=true, desc="交易密码")
* @Apidoc\Param("code", type="string",require=true, desc="图形验证码(event=cdkeyExchange)")
*/
public function cdkeyExchange_local_cdkey(){
//return $this->error(__('The system is under maintenance, please wait...'));
$user = \support\Jwt\JwtToken::getUser();
// if(Config('site.trade_password_type') == 'email'){
// captcha_verfiy('email','exchange',$user['username']);
// }else{
// $trade_password = input('trade_password');
// \support\Jwt::verify_trade_password($trade_password);
// }
$cdkey = input('cdkey');
/** @var CdkeyModel $Cdkey */
$Cdkey = CdkeyModel::where('account',$cdkey)->lock(true)->where('is_used',0)->find();
if(!$Cdkey){
return $this->error(__('卡密不存在'));
}
if($Cdkey['type'] == 3){
//不能使用续费激活码
return $this->error(__('卡密不存在'));
}
Db::startTrans();
try{
CdkeyModel::where('id',$Cdkey->id)->save([
'record_id' => $user->id,
'is_used' => 1,
'use_time' => time(),
]);
UserModel::score($user->id,$Cdkey->days,\app\enum\BalanceType::RECHARGE_CARD);
Db::commit();
return $this->success(__('Exchange successful'));
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
}
/**
* cdkey兑换
* @Apidoc\Method("POST")
* @Apidoc\Param("card_number", type="string",require=true, desc="卡号")
* @Apidoc\Param("password", type="string",require=true, desc="密码")
*/
function cdkey_exchange(){
$user = \support\Jwt\JwtToken::getUser();
$domain = 'http://127.0.0.1:8383';
$data=[
'user_id' => \support\Jwt\JwtToken::getCurrentId(),
'card_number'=> input('card_number'),
'password'=> input('password'),
];
$activeData = [
'app_id' => 8,
'card_number' => $data['card_number'],
'password' => $data['password'],
'type' => 'recharge',
'record_id' => $user->id
];
$remoteResponse = post($domain.'/api/cdkey/redeem',$activeData);
\support\Log::info($remoteResponse);
try{
$remoteResponse = json_decode($remoteResponse,true);
}catch(\Exception $e){
return $this->error($e->getMessage());
}
if($remoteResponse['code'] !== 0){
\support\Log::info(json_encode($remoteResponse));
return $this->error($remoteResponse['msg']);
}
if($remoteResponse['data']['days']){
UserModel::money($user->id,$remoteResponse['data']['days'],\app\enum\BalanceType::RECHARGE_CARD);
return $this->success(__('Exchange successful'));
}
return $this->error($remoteResponse['msg'],$remoteResponse);
}
}
@@ -1,186 +0,0 @@
<?php
namespace app\api\controller;
use app\model\Address as AddressModel;
use app\model\User as UserModel;
use app\model\Withdrawl as WithdrawlModel;
use support\Request;
use support\think\Db;
use taoser\facade\Validate;
use hg\apidoc\annotation as Apidoc;
/**
* 提现模块
*/
class WithdrawlController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = ['notify','notify1','recent'];
/**
* 列表
* @Apidoc\Method("GET")
* @Apidoc\Query("status", type="int", require=false, desc="状态")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list()
{
$limit = (int)input('limit',10);
$status = input('status','all');
$model = WithdrawlModel::where('user_id',\support\Jwt\JwtToken::getCurrentId())
->order('id desc');
if($status!='all'){
$model = $model->where('status',$status);
}
$list = $model->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
* 最近提现
* @Apidoc\Method("GET")
*/
public function recent()
{
$list = WithdrawlModel::with(['user'])->
where('status',\app\enum\WithdrawlStatus::COMPLETE->value)->
order('id desc')->
limit(10)->select();
$list->each(function($item){
$item->user = UserModel::field('username,email')->where('id',$item->user_id)->find();
});
return $this->success(__('successful'),$list->toArray());
}
/**
* 创建
* @Apidoc\Method("POST")
* @Apidoc\Param("amount", type="string", require=true, desc="金额")
* @Apidoc\Param("address_id", type="string", require=true, desc="地址ID,列表选择")
* @Apidoc\Param("trade_password", type="string", require=true, desc="交易密码")
*/
public function create()
{
//return $this->error(__('The system is under maintenance, please wait...'));
//* @Apidoc\Param("code", type="string", require=true, desc="图形验证码(type=withdrawl)")
//captcha_verfiy('image','withdrawl');
$address_id = input('address_id');
if(!$address_id){
return $this->error(__('Address is incorrect'));
}
/** @var AddressModel $address */
$address = AddressModel::where('id',$address_id)->find();
if(!$address){
return $this->error(__('Address is incorrect'));
}
// if(!$address->status){
// return $this->error(__('Unverified address'));
// }
$user = \support\Jwt::getUser();
if(Config('site.trade_password_type') == 'email'){
captcha_verfiy('email','withdrawl',$user['username']);
}else{
//验证交易密码
$trade_password = input('trade_password');
\support\Jwt::verify_trade_password($trade_password);
}
$deduction_amount = input('amount',0);
$fee = config('site.withdrawl_fee')[$address['network']];
if($fee < 0.5){
$fee = bcmul( $fee , $deduction_amount,2);
}
$data = [
'user_id' => \support\Jwt\JwtToken::getCurrentId(),
'deduction_amount' => $deduction_amount,
'title' => $address['title'],
'network' => $address['network'],
'address' => $address['address'],
'fee' => $fee,
'type' => 0,
'status' => \app\enum\WithdrawlStatus::CREATED->value
];
//验证最小提现金额
$data['recive_amount'] = $data['deduction_amount'] - $data['fee'];
$withdrawl_minimum = Config('site.withdrawl_minimum')[$data['network']];
if($data['deduction_amount'] < $withdrawl_minimum){
return $this->error(__('Minimum withdrawal of %num%',[
'%num%' => $withdrawl_minimum
]));
}
//var_dump($user);
//验证余额
if($data['deduction_amount'] > $user->money){
return $this->error(__('The amount exceeds the available balance'));
}
//if(WithdrawlModel::whereTime('created_at','-24 hours')->count('id')){
if(WithdrawlModel::whereTime('created_at','today')->where('user_id',$data['user_id'])->count('id')){
return $this->error(__('You can only withdraw once a day.'));
}
if (!$data['network'] || !in_array($data['network'],['BEP-20','TRC-20','ALIPAY','WECHAT'])) {
return $this->error(__('Network is incorrect'));
}
if (!$data['address']) {
return $this->error(__('Address is incorrect'));
}
Db::startTrans();
try{
/** @var WithdrawlModel $data */
$data = WithdrawlModel::create($data);
UserModel::money($data->user_id,-$data->deduction_amount,\app\enum\BalanceType::WITHDRAWAL,$data->id);
Db::commit();
return $this->success(__('successful'),$data);
}catch(\Exception $e){
Db::rollback();
return $this->error($e->getMessage());
}
}
/**
* 详情
* @Apidoc\Query("id", type="string", require=true, desc="ID")
*/
public function detail(){
$appid = input('id');
$vo = WithdrawlModel::where('id',$appid)->find();
if($vo) {
return $this->success(__('successful'),$vo->toArray());
}else{
return $this->error(__("Record does not exist"));
}
}
/**
* 转账成功异步通知
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
public function notify(){
$data = aesdecode(input('data',''));
$data = json_decode($data,true);
/** @var WithdrawlModel $vo */
$vo = WithdrawlModel::where('id',$data['out_trade_no'])->find();
if($vo){
if($data['result'] == 'SUCCESS'){
if($vo->status != \app\enum\WithdrawlStatus::COMPLETE->value){
$vo->status = \app\enum\WithdrawlStatus::COMPLETE->value;
$vo->txid = $data['txid'];
$vo->transfer_at = $data['transfer_at'] ?: time();
$vo->save();
Hook('withdrawl.success',$vo);
}
}else{
$vo->status = \app\enum\WithdrawlStatus::FAIL->value;
$vo->txid = $data['txid'];
$vo->memo = $data['reason'];
$vo->save();
}
}
return response("SUCCESS");
}
}
-150
View File
@@ -1,150 +0,0 @@
<?php
namespace app\api\middleware;
use ReflectionException;
use support\exception\BusinessException;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
use support\Container;
class Auth implements MiddlewareInterface
{
/**
* @param Request $request
* @param callable $handler
* @return Response
* @throws ReflectionException|BusinessException
*/
public function process(Request $request, callable $next): Response
{
if($request->method() == 'OPTIONS'){
$headers = [
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Origin' => $request->header('origin', '*'),
'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
];
$response = response('200',200,$headers);
return $response;
}
$lang = $request->input('lang','zh-Hans');
locale($lang);
if ($request->controller) {
$request->client = $request->header('client',"web");
// if($request->client=='win' && $request->header('version') < 2.06){
// abort('旧版本不能再使用,请更新到最新版本', 603);
// }
//跨域请求检测
//check_cors_request();
// 检测IP是否允许
//check_ip_allowed();
$request->start_time = microtime();
$controller = Container::get($request->controller);
// 检测是否需要验证登录
if (!\support\Jwt::match($controller->noNeedLogin)) {
//检测是否登录
try {
if (!\support\Jwt::isLogin()) {
return json([
"code"=>401,
"data"=>[],
"msg"=>__('Please login first')
]);
}
} catch (\Exception $e) {
return json([
"code"=>401,
"data"=>[],
"msg"=>__('Please login first')
]);
}
$user = \support\Jwt\JwtToken::getUser();
if(!$user['status']){
return json([
"code"=>403,
"data"=>[],
"msg"=>__('Account is locked')
]);
}
// $key = "debounce_" . $request->path() . "_" . ($user->id ?? 'guest');
// $ttl = 1; // 防抖时间(秒)
// $redishandler = new \Redis;
// $redishandler->connect(
// \support\Env::get('host'),
// (int) \support\Env::get('port'),
// (int) \support\Env::get('timeout'));
// $redishandler->select(12);
// if ($redishandler->setnx($key, 1)) {
// $redishandler->expire($key, $ttl);
// }else{
// return new Response(429,[],__('Too frequent operation'));
// }
// 判断是否需要验证权限
if (!\support\Jwt::match($controller->noNeedAuth)) {
// 判断控制器和方法判断是否有对应权限
$controllername = get_controller_name();
$actionname = strtolower(get_action_name());
$path = str_replace('.', '/', $controllername) . '/' . $actionname;
if (!\support\Jwt::check($path)) {
return json([
"code"=>405,
"data"=>[],
"msg"=>__('You have no permission')
]);
}
}
}
if($request->client!='web'){
$data = $request->post('data');
if($data){
$data = str_replace('%3D','=',$data);
$data = str_replace(' ','+',$data);
//var_dump($data);
$data = aesdecode($data);
$data = json_decode($data,true);
//var_dump($data);
$request->withBody($data);
}
}
$config = Config('site');
$config['debug'] = config('app.debug');
$config['controller'] = $request->controller_name;
$config['action'] = $request->action_name;
$request->_view_vars = array_merge((array) $request->_view_vars,[
'user' => session('admin'),
'config' => $config
]);
$response = $next($request);
$headers = [
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Origin' => $request->header('origin', '*'),
'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
];
$response->withHeaders($headers);
//cp('auth');
//\support\Log::alert('auth');
$body = str_replace([
'__SELF__'
],[
request()->path()
],$response->rawBody());
if($request->app=="api" && $request->client!='web'){
$body = aesencode($body);
}
$response->withBody($body)->getStatusCode();
$time = microtime() - $request->start_time;
//echo("响应时间:".$request->uri().':'.$time.PHP_EOL);
//$response = $next($request);
//\support\Log::error($response->rawBody());
return $response;
}
return $next($request);
}
}
-51
View File
@@ -1,51 +0,0 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\bootstrap;
use Webman\Bootstrap;
//use support\Db;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use support\think\Db;
class SqlDebug implements Bootstrap
{
/**
* 自定义输出格式,否则输出前面会带有当前文件,无用信息
* @param $var
* @return void
*/
public static function dumpvar($var): void
{
$cloner = new VarCloner();
$dumper = new CliDumper();
$dumper->dump($cloner->cloneVar($var));
}
public static function start($worker)
{
// Is it console environment ?
$is_console = !$worker;
// if ($is_console) {
// return;
// }
if (!Config("app.debug")) return;
Db::listen(function($sql, $runtime, $master) {
if (!Config("app.debug")) return;
if($sql!='select 1' && $sql){
$sql= preg_replace('/db\.[db\.]+/', 'db.', $sql);
\support\Log::alert('['.$runtime.']'.$sql);
}
});
}
}
-47
View File
@@ -1,47 +0,0 @@
<?php
namespace app\command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use app\model\BalanceLog;
class BalanceLogTask extends Command
{
protected static $defaultName = 'balance:log-task';
protected static $defaultDescription = 'balance:log-task';
protected function configure()
{
}
protected function execute(InputInterface $input, OutputInterface $output):int
{
// 1. 确保索引存在
$output->writeln('Creating indexes...');
$indexResults = BalanceLog::createAllIndexes();
foreach ($indexResults as $currency => $messages) {
$output->writeln("[$currency]");
foreach ($messages as $message) {
$output->writeln(" - $message");
}
}
// 2. 执行数据归档
$output->writeln('Archiving old data...');
$archiveResults = BalanceLog::archiveData(3); // 归档3天前的数据
foreach ($archiveResults as $currency => $result) {
$output->writeln("[$currency]");
$output->writeln(" - Table: {$result['table']}");
$output->writeln(" - Archived: {$result['archived']} records");
foreach ($result['messages'] as $message) {
$output->writeln(" - $message");
}
}
$output->writeln('All tasks completed!');
return self::SUCCESS;
}
}
-60
View File
@@ -1,60 +0,0 @@
<?php
namespace app\command;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use think\db\PDOConnection;
use support\think\Db;
class Clear extends Command
{
protected static $defaultName = 'clear';
protected static $defaultDescription = '数据库缓存';
protected function configure()
{
$this->setDescription('clear database.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$action = 'all';
if($action == 'all'){
Db::name('address')->where('id','>',0)->delete();
Db::name('recharge')->where('id','>',0)->delete();
Db::name('transfer')->where('id','>',0)->delete();
Db::name('user')->where('id','>',0)->delete();
Db::name('user_extend')->where('user_id','>',0)->delete();
Db::name('user_team')->where('descendant_id|ancestor_id','>',0)->delete();
Db::name('withdrawl')->where('id','>',0)->delete();
Db::name('work_record')->where('id','>',0)->delete();
}else{
$list = \app\model\User::order('id','asc')->select();
foreach($list as $k=>$user){
Db::name('address')->where('user_id',$user->id)->delete();
Db::name('transfer')->where('user_id',$user->id)->delete();
Db::name('recharge')->where('user_id',$user->id)->delete();
Db::name('record')->where('user_id',$user->id)->delete();
Db::name('withdrawl')->where('user_id',$user->id)->delete();
Db::name('user_extend')->where('user_id',$user->id)->delete();
Db::name('user_team')->where('descendant_id|ancestor_id','=',$user->id)->delete();
Db::name('withdrawl')->where('user_id',$user->id)->delete();
Db::name('work_record')->where('user_id',$user->id)->delete();
Db::name('user')->where('id',$user->id)->delete();
}
}
$output->writeln('<info>Succeed!</info>');
return self::SUCCESS;
}
protected function buildModelSchema(string $class): void
{
}
}
-135
View File
@@ -1,135 +0,0 @@
<?php
namespace app\command;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use support\think\Db;
use app\model\User as UserModel;
class Database extends Command
{
protected static $defaultName = 'Db';
protected static $defaultDescription = 'Database 优化';
/**
* @return void
*/
protected function configure()
{
$this->addOption('action','a', InputArgument::OPTIONAL, '要做什么操作');
$this->addOption('table','t', InputArgument::OPTIONAL, '表名');
$this->addOption('domain','ym', InputArgument::OPTIONAL, 'domain');
$this->addOption('robot_id','rid', InputArgument::OPTIONAL, 'robot_id');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$action = $input->getOption('action');
if($action == 'prototype'){
return $this->prototype($input, $output);
}
cp('操作不存在:'.$action);
return 0;
}
function prototype(InputInterface $input, OutputInterface $output){
$table = $input->getOption('table');
// 获取表前缀并构建完整表名
$prefix = config('thinkorm.connections.mysql.prefix', '');
$fullTableName = '`' . $prefix . $table . '`';
// 查询表结构
$res = Db::query('SHOW FULL COLUMNS FROM ' . $fullTableName);
if (empty($res)) {
return "// 表 {$table} 不存在或没有字段";
}
$annotations = [];
$annotations[] = '/**';
foreach ($res as $row) {
$field = $row['Field'];
$type = $row['Type'];
$comment = $row['Comment'] ?: '无注释';
// 处理字段类型映射
$phpType = $this->mapMysqlTypeToPhp($type);
// 处理特殊字段
if ($field === 'id') {
$annotations[] = " * @property integer \${$field} 主键(ID) - {$comment}";
} else {
$annotations[] = " * @property {$phpType} \${$field} {$comment}";
}
}
$annotations[] = ' */';
cp( implode("\n", $annotations));
return self::SUCCESS;
}
/**
* 将MySQL字段类型映射到PHP类型
*
* @param string $mysqlType MySQL字段类型
* @return string PHP类型
*/
protected function mapMysqlTypeToPhp($mysqlType)
{
$mysqlType = strtolower($mysqlType);
// 整数类型
if (preg_match('/^(tinyint|smallint|mediumint|int|bigint)/', $mysqlType)) {
// 检查是否为无符号
if (strpos($mysqlType, 'unsigned') !== false) {
return 'integer'; // 无符号整数也返回integer
}
return 'integer';
}
// 浮点类型
if (preg_match('/^(float|double|decimal)/', $mysqlType)) {
return 'float';
}
// 字符串类型
if (preg_match('/^(varchar|char|text|tinytext|mediumtext|longtext|enum|set)/', $mysqlType)) {
return 'string';
}
// 日期时间类型
if (preg_match('/^(date|time|datetime|timestamp|year)/', $mysqlType)) {
return 'string'; // 或者可以返回 '\\DateTime' 如果需要更精确的类型
}
// 二进制类型
if (preg_match('/^(blob|tinyblob|mediumblob|longblob|binary|varbinary)/', $mysqlType)) {
return 'string'; // 或者根据需求返回其他类型
}
// JSON类型
if (strpos($mysqlType, 'json') !== false) {
return 'array'; // 或者 'mixed'
}
// 布尔类型(tinyint(1)通常用作布尔值)
if ($mysqlType === 'tinyint(1)' || $mysqlType === 'boolean' || $mysqlType === 'bool') {
return 'boolean';
}
// 默认返回混合类型
return 'mixed';
}
}
-101
View File
@@ -1,101 +0,0 @@
<?php
namespace app\command;
use Exception;
use plugin\admin\app\model\Config;
use support\Request;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use support\think\Db;
class Language extends Command
{
protected static $defaultName = 'Language:scan';
protected static $defaultDescription = '自动完成多语言的文件提取';
/**
* @return void
*/
protected function configure()
{
$this->addOption('file','f', InputArgument::OPTIONAL, '只是针对那个文件');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$file = $input->getOption('file');
$file = base_path()."/app/api/controller/ServerController.php";
$dir = new \RecursiveDirectoryIterator(base_path().'/app');
$iterator = new \RecursiveIteratorIterator($dir);
$phpFiles = new \RegexIterator($iterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
$fnlist = [];
$result = [
'common.php'=>""
];
foreach ($phpFiles as $_file) {
$_file = $_file[0];
$fnlist[] = $_file;
$key = 'common.php';
if(false !==strpos($_file, base_path().'/app/api/controller')){
$key = 'api/'.pathinfo($_file,PATHINFO_BASENAME);
}
if(false !==strpos($_file, base_path().'/app/controller')){
$key = pathinfo($_file,PATHINFO_BASENAME);
}
$key = strtolower(str_replace('Controller','',$key));
//cp($key);
$res = $this->parseOneFile($_file);
$result[$key]=$res;
}
//$res = $this->parseOneFile($file);
//cp($result);
$this->write2file($result);
return 0;
}
function write2file($data=[]){
$langs = ['zh','en'];
foreach($data as $fn=>$arr){
foreach($langs as $lang){
$lang_path = base_path().'/resource/translations/'.$lang.'/';
$_common_arr = require($lang_path.'common.php');
$_arr = [];
if(file_exists($lang_path.$fn)){
$_arr = require($lang_path.$fn);
}
foreach($arr as $ov){
if(!isset($_common_arr[$ov]) && !isset($_arr[$ov])){
$_arr[$ov]=$ov;
}
if(isset($_common_arr[$ov]) && isset($_arr[$ov])){
unset($_arr[$ov]);
}
}
file_put_contents($lang_path.$fn,'<?php'.PHP_EOL.'return '.var_export($_arr,true).';');
//cp('写入文件:'.$lang_path.$fn);
}
}
}
function parseOneFile($fn){
cp('解析文件:',$fn);
if(file_exists($fn)){
$content = file_get_contents($fn);
$matchs = [];
preg_match_all('/__\(([\'"])(.*?)\1\s*(?:,\s*\[.*?\])?\)/',$content,$matchs);
//cp($matchs[2]);
return $matchs[2];
}else{
cp('文件不存在:'.$fn);
}
}
}
-48
View File
@@ -1,48 +0,0 @@
<?php
namespace app\command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use support\think\Db;
class Otop extends Command
{
protected static $defaultName = 'Otop';
protected static $defaultDescription = '结算';
/**
* @return void
*/
protected function configure()
{
$this->addOption('user_id','uid', InputArgument::OPTIONAL, 'user_id');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$user_id = $input->getOption('user_id');
if(!$user_id){
return false;
}
/**
* @var \plugin\admin\app\model\Admin $admin
*/
$admin = \plugin\admin\app\model\Admin::where('id',$user_id)->find();
if(!$admin){
return false;
}
$totp = \OTPHP\TOTP::create($admin->totp_secret);
cp($totp->now());
return 1;
}
}
-36
View File
@@ -1,36 +0,0 @@
<?php
namespace app\command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use support\think\Db;
class Settlement extends Command
{
protected static $defaultName = 'Settlement';
protected static $defaultDescription = '结算';
/**
* @return void
*/
protected function configure()
{
$this->addArgument('name', InputArgument::OPTIONAL, 'Name description');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
(new \support\Settlement())->autoSettlement();
return 1;
}
}
-187
View File
@@ -1,187 +0,0 @@
<?php
namespace app\command;
use Exception;
use plugin\admin\app\model\Config;
use app\model\User as UserModel;
use support\Request;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use support\think\Db;
use support\Mattermost;
use app\model\User;
class Tongji extends Command
{
protected static $defaultName = 'Tongji';
protected static $defaultDescription = 'Tongji';
/**
* @return void
*/
protected function configure()
{
$this->addOption('action','a', InputArgument::OPTIONAL, '要做什么操作');
$this->addOption('user_id','uid', InputArgument::OPTIONAL, 'user_id');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->question($input,$output);
//$this->role($input,$output);
//$this->recharge($input,$output);
//$this->withdrawl($input,$output);
return self::SUCCESS;
}
/**
* 修复充值统计
*/
function recharge(InputInterface $input, OutputInterface $output) {
//购买金额统计
$recharge = \app\model\Recharge::where('status',\app\enum\RechargeStatus::COMPLETE->value)->select();
$recharge_result = [];
$statistics_recharge_times_result = [];
$user_recharge_total_result = [];
$team_recharge_total_result = [];
/**
* @var \app\model\Recharge $vo
*/
foreach($recharge as $vo){
$date = explode(' ',$vo->created_at)[0];
$recharge_result[$date] += abs($vo->amount);
$statistics_recharge_times_result[$date] += 1;
$user_recharge_total_result[$vo->user_id.''] +=abs($vo->amount);
$parent_id = get_parent_id($vo->user_id);
if($parent_id){
//团队提现统计
$team_recharge_total_result[$parent_id.''] +=abs($vo->amount);
}
}
foreach($recharge_result as $date => $value){
cache('statistics_recharge_amount_'.$date,$value);
}
foreach($statistics_recharge_times_result as $date => $value){
cache('statistics_recharge_times_'.$date,$value);
}
foreach($user_recharge_total_result as $user_id => $value){
cache('user_recharge_total_'.$user_id,$value);
}
foreach($team_recharge_total_result as $user_id => $value){
cache('team_recharge_total_'.$user_id,$value);
}
cp($recharge_result);
}
/**
* 修复提现统计
*/
function withdrawl(InputInterface $input, OutputInterface $output) {
//购买金额统计
$withdrawl = \app\model\Withdrawl::where('status',\app\enum\WithdrawlStatus::COMPLETE->value)->select();
$withdrawl_result = [];
$statistics_withdrawl_times_result = [];
$user_withdrawl_total_result = [];
$team_withdrawl_total_result = [];
/**
* @var \app\model\Withdrawl $vo
*/
foreach($withdrawl as $vo){
$date = explode(' ',$vo->created_at)[0];
$withdrawl_result[$date] += abs($vo->recive_amount);
$statistics_withdrawl_times_result[$date] += 1;
$user_withdrawl_total_result[$vo->user_id.''] +=abs($vo->recive_amount);
$parent_id = get_parent_id($vo->user_id);
if($parent_id){
//团队提现统计
$team_withdrawl_total_result[$parent_id.''] +=abs($vo->recive_amount);
}
}
foreach($withdrawl_result as $date => $value){
cache('statistics_withdrawl_amount_'.$date,$value);
}
foreach($statistics_withdrawl_times_result as $date => $value){
cache('statistics_withdrawl_times_'.$date,$value);
}
foreach($user_withdrawl_total_result as $user_id => $value){
cache('user_withdrawl_total_'.$user_id,$value);
}
foreach($team_withdrawl_total_result as $user_id => $value){
cache('team_withdrawl_total_'.$user_id,$value);
}
cp($withdrawl_result);
}
function question(InputInterface $input, OutputInterface $output) {
$order_list = \app\model\ProductOrder::withJoin([
'product'=>function($q){
return $q->field('price,interest_rate');
}
])->where('quantity','>',0)->select();
}
/**
* 修复所有角色购买统计
*/
function role(InputInterface $input, OutputInterface $output) {
}
/**
* 模拟用户注册
*/
function register(InputInterface $input, OutputInterface $output){
$last_user_id = UserModel::order('id','desc')->limit(1)->value('id');
for ($i=$last_user_id+1; $i <= $last_user_id+2; $i++) {
$uids = UserModel::where("status",1)->column('id');
$referrerId = $uids[array_rand($uids)];
$email = 'test'.$i.'@msn.cn';
$mobile = '';
$password = '123456';
$extends = [
'role_id' => rand(1,3),
'money' => 0,
'parent_id' => $referrerId
];
$user = \support\Jwt::register($email, $password, $email, $mobile, $extends);
cp($user['id']);
}
return 1;
}
function updateRechargeAddress(InputInterface $input, OutputInterface $output){
$saveData = [];
$res = post(Config('pay.server').'/RechargeAddress/create',['appid'=>Config('pay.appid')]);
if($res){
$res = json_decode($res,true);
if($res['code'] === 0){
$saveData['bep_recharge_address'] = $res['data']['BEP-20']['address'];
$saveData['trc_recharge_address'] = $res['data']['TRC-20']['address'];
$saveData['decimal_part'] = $res['data']['BEP-20']['decimal_part'];
}
}
UserModel::where('id',123409)->update($saveData);
return 0;
}
function otop(){
$secret = 'EJGYB7OZR2W46XRX7VB3PXHSOY4LUAWCA5GTDAVTWKHXNDAAAIIP7AQ3JSO3XZJNX5J5OTIDEQVKLYFYIYNAXSCYF4GNZ2EMA4ORA3Y';
$totp = \OTPHP\TOTP::create($secret);
$secret = $totp->getSecret();
$totp->setLabel('cansnow');
$totp->setIssuer('DVPN');
$qrCodeUri =$totp->getProvisioningUri();
cp($secret);
cp($qrCodeUri);
cp('https://api.qrtool.cn/?text='.urlencode($qrCodeUri));
cp($totp->at(time()));
if ($totp->verify('535714')) {
cp('验证成功');
} else {
cp('验证失败');
}
}
}
-37
View File
@@ -1,37 +0,0 @@
<?php
namespace app\command;
use Monolog\Formatter\MongoDBFormatter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use support\think\Db;
class createIndex extends Command
{
protected static $defaultName = 'createIndex';
protected static $defaultDescription = 'createIndex';
/**
* @return void
*/
protected function configure()
{
$this->addArgument('name', InputArgument::OPTIONAL, 'Name description');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
\app\model\BalanceLog::createindex();
return 0;
}
}
-46
View File
@@ -1,46 +0,0 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\api\exception;
use Throwable;
use Webman\Exception\ExceptionHandler;
use Webman\Http\Request;
use Webman\Http\Response;
/**
* Class Handler
* @package sapp\api\exception
*/
class Handler extends ExceptionHandler
{
public $dontReport = [
\support\exception\BusinessException::class,
];
public function report(Throwable $exception)
{
parent::report($exception);
}
public function render(Request $request, Throwable $exception): Response
{
$code = $exception->getCode();
$json = ['code' => $code ?: 500, 'msg' => $exception->getMessage()];
return new Response(200, ['Content-Type' => 'application/json'],
json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
}
-67
View File
@@ -1,67 +0,0 @@
<?php
namespace app\controller;
use think\Model;
use support\Response;
/**
* 基础控制器
*/
class Base
{
/**
* @var Model
*/
protected $model = null;
/**
* 无需登录及鉴权的方法
* @var array
*/
protected $noNeedLogin = [];
/**
* 需要登录无需鉴权的方法
* @var array
*/
protected $noNeedAuth = [];
/**
* 数据限制
* null 不做限制,任何管理员都可以查看该表的所有数据
* auth 管理员能看到自己以及自己的子管理员插入的数据
* personal 管理员只能看到自己插入的数据
* @var string
*/
protected $dataLimit = null;
/**
* 数据限制字段
*/
protected $dataLimitField = 'admin_id';
/**
* 返回格式化json数据
*
* @param int $code
* @param string $msg
* @param array $data
* @return Response
*/
protected function json(int $code, string $msg = 'ok', array $data = []): Response
{
return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
}
protected function success(string $msg = '成功', array $data = []): Response
{
return $this->json(0, $msg, $data);
}
protected function fail(string $msg = '失败', array $data = []): Response
{
return $this->json(1, $msg, $data);
}
}
-440
View File
@@ -1,440 +0,0 @@
<?php
namespace app\controller;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder as QueryBuilder;
use support\exception\BusinessException;
use support\think\Model;
use support\Request;
use support\Response;
use support\think\Db;
class Crud extends Base
{
/**
* @var Model
*/
protected $model = null;
/**
* 查询
* @param Request $request
* @return Response
* @throws BusinessException
*/
public function select(Request $request): Response
{
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
$query = $this->doSelect($where, $field, $order);
return $this->doFormat($query, $format, $limit);
}
/**
* 添加
* @param Request $request
* @return Response
* @throws BusinessException
*/
public function insert(Request $request): Response
{
$data = $this->insertInput($request);
$id = $this->doInsert($data);
return $this->success(__('successful'), ['id' => $id]);
}
/**
* 更新
* @param Request $request
* @return Response
* @throws BusinessException
*/
public function update(Request $request): Response
{
[$id, $data] = $this->updateInput($request);
$this->doUpdate($id, $data);
return $this->success(__('successful'));
}
/**
* 删除
* @param Request $request
* @return Response
* @throws BusinessException
*/
public function delete(Request $request): Response
{
$ids = $this->deleteInput($request);
$this->doDelete($ids);
return $this->success(__('successful'));
}
/**
* 查询前置
* @param Request $request
* @return array
* @throws BusinessException
*/
protected function selectInput(Request $request): array
{
$field = $request->get('sort');
$order = $request->get('sortOrder', 'asc');
$format = $request->get('format', 'normal');
$limit = (int)$request->get('limit', $format === 'tree' ? 1000 : 10);
$limit = $limit <= 0 ? 10 : $limit;
$order = $order === 'asc' ? 'asc' : 'desc';
$where = $request->get('filter',[]);
$page = (int)$request->get('page');
$page = $page > 0 ? $page : 1;
$allow_column = [];
//var_dump($this->model->getConnectionName());
//if ($this->model->getConnection()->getDriverName() == 'mongodb') {
if ($this->model->getConnection() != 'mysql') {
} else {
$table = $this->model->getTable();
$allow_column = Db::query("desc `$table`");
if (!$allow_column) {
throw new BusinessException('表不存在');
}
$allow_column = array_column($allow_column, 'Field', 'Field');
if (!in_array($field, $allow_column)) {
$field = null;
}
}
return [$where, $format, $limit, $field, $order, $page];
}
/**
* 指定查询where条件,并没有真正的查询数据库操作
* @param array $where
* @param string|null $field
* @param string $order
* @return Model
*/
protected function doSelect(array $where, string $field = null, string $order = 'desc')
{
$model = $this->model;
foreach ($where as $column => $value) {
$symbol = $value['symbol'];
$value1 = $value['value1'];
$value2 = $value['value2'];
if (is_array($value)) {
if ($symbol === 'like' || $symbol === 'not like') {
$model = $model->where($column, $symbol, "%$value1%");
} elseif (in_array($symbol, ['>', '=', '<', '<>','>=','<='])) {
$model = $model->where($column, $symbol, $value1);
} elseif (($symbol == 'in'|| $symbol == 'not in') && !empty($value1)) {
$valArr = $value1;
if (is_string($value1)) {
$valArr = explode(",", trim($value1));
}
if($symbol == 'in'){
$model = $model->whereIn($column, $valArr);
}else{
$model = $model->whereNotIn($column, $valArr);
}
} elseif ($symbol == 'null') {
$model = $model->whereNull($column);
} elseif ($symbol == 'not null') {
$model = $model->whereNotNull($column);
} elseif ($symbol == 'range' && $$value1 !== '' || $value2 !== '') {
$model = $model->whereBetween($column, [$value1, $value2]);
}
} else {
$model = $model->where($column, $value);
}
}
if ($field) {
$model = $model->order($field, $order);
}
return $model;
}
/**
* 执行真正查询,并返回格式化数据
* @param $query
* @param $format
* @param $limit
* @return Response
*/
protected function doFormat($query, $format, $limit,$fields="*"): Response
{
$methods = [
'select' => 'formatSelect',
'tree' => 'formatTree',
'table_tree' => 'formatTableTree',
'normal' => 'formatNormal',
];
if($limit == 'all'){
$paginator = $query->field($fields)->select();
$total = count($paginator);
$items = $paginator;
}else{
$paginator = $query->field($fields)->paginate($limit);
$total = $paginator->total();
$items = $paginator->items();
}
if (method_exists($this, "afterQuery")) {
$items = call_user_func([$this, "afterQuery"], $items);
}
$format_function = $methods[$format] ?? 'formatNormal';
return call_user_func([$this, $format_function], $items, $total);
}
/**
* 插入前置方法
* @param Request $request
* @return array
* @throws BusinessException
*/
protected function insertInput(Request $request): array
{
$data = $this->inputFilter($request->post());
$password_filed = 'password';
if (isset($data[$password_filed])) {
$data[$password_filed] = password_hash($data[$password_filed],PASSWORD_DEFAULT);
}
return $data;
}
/**
* 执行插入
* @param array $data
* @return mixed|null
*/
protected function doInsert(array $data)
{
$primary_key = $this->model->getPk();
$model_class = get_class($this->model);
$model = $model_class::create($data);
return $primary_key ? $model->$primary_key : null;
}
/**
* 更新前置方法
* @param Request $request
* @return array
* @throws BusinessException
*/
protected function updateInput(Request $request): array
{
$primary_key = $this->model->getPk();
$id = $request->post($primary_key);
$data = $this->inputFilter($request->post());
$model = $this->model->find($id);
if (!$model) {
throw new BusinessException('记录不存在', 2);
}
$password_filed = 'password';
if (isset($data[$password_filed])) {
// 密码为空,则不更新密码
if ($data[$password_filed] === '') {
unset($data[$password_filed]);
} else {
$data[$password_filed] = password_hash($data[$password_filed],PASSWORD_DEFAULT);
}
}
unset($data[$primary_key]);
return [$id, $data];
}
/**
* 执行更新
* @param $id
* @param $data
* @return void
*/
protected function doUpdate($id, $data)
{
$model = $this->model->find($id);
foreach ($data as $key => $val) {
$model->{$key} = $val;
}
$model->save();
}
/**
* 对用户输入表单过滤
* @param array $data
* @return array
* @throws BusinessException
*/
protected function inputFilter(array $data): array
{
$table = config('database.connections.mysql.prefix') . $this->model->getTable();
$allow_column = Db::getFields($this->model->getTable());
if (!$allow_column) {
throw new BusinessException('表不存在', 2);
}
//$columns = array_column($allow_column, 'Type', 'Field');
//echo json_encode($allow_column);
foreach ($data as $col => $item) {
if (!isset($allow_column[$col])) {
unset($data[$col]);
continue;
}
// 非字符串类型传空则为null
if ($item === '' && strpos(strtolower($allow_column[$col]['type']), 'varchar') === false && strpos(strtolower($allow_column[$col]['type']), 'text') === false) {
$data[$col] = null;
}
if (is_array($item)) {
$data[$col] = implode(',', $item);
}
}
if (empty($data['created_at'])) {
unset($data['created_at']);
}
if (empty($data['updated_at'])) {
unset($data['updated_at']);
}
return $data;
}
/**
* 删除前置方法
* @param Request $request
* @return array
* @throws BusinessException
*/
protected function deleteInput(Request $request): array
{
$primary_key = $this->model->getPk();
if (!$primary_key) {
throw new BusinessException('该表无主键,不支持删除');
}
$ids = $request->post('ids', '');
if(!is_array($ids)){
$ids = explode(',',$ids);
}
return $ids;
}
/**
* 执行删除
* @param array $ids
* @return void
*/
protected function doDelete(array $ids)
{
if (!$ids) {
return;
}
$primary_key = $this->model->getPk();
$this->model->whereIn($primary_key, $ids)->delete();
}
/**
* 格式化树
* @param $items
* @return Response
*/
protected function formatTree($items): Response
{
$format_items = [];
//$primary_key = $this->model->getPk();
$primary_key = $this->model->getPk();
foreach ($items as $item) {
$item->name = $this->guessName($item) ?: $item->$primary_key;
$item->value = (string)$item->$primary_key;
$item->id = $item->$primary_key;
//$item->pid = $item->pid;
$format_items[] = $item;
}
return $this->success(__('successful'), $format_items);
}
/**
* 格式化表格树
* @param $items
* @return Response
*/
protected function formatTableTree($items): Response
{
return $this->success(__('successful'), $items);
}
/**
* 格式化下拉列表
* @param $items
* @return Response
*/
protected function formatSelect($items): Response
{
$formatted_items = [];
$primary_key = $this->model->getPk();
foreach ($items as $item) {
$formatted_items[] = [
'name' => $this->guessName($item) ?: $item->$primary_key,
'value' => $item->$primary_key
];
}
return $this->success(__('successful'), $formatted_items);
}
/**
* 通用格式化
* @param $items
* @param $total
* @return Response
*/
protected function formatNormal($items, $total): Response
{
return json(['code' => 0, 'msg' => 'ok', 'count' => $total, 'data' => $items]);
}
/**
* 查询数据库后置方法,可用于修改数据
* @param mixed $items 原数据
* @return mixed 修改后数据
*/
protected function afterQuery($items)
{
return $items;
}
/**
* 猜测记录名称
* @param $item
* @return mixed
*/
protected function guessName($item)
{
return $item->title ?? $item->name ?? $item->nickname ?? $item->username ?? $item->id;
}
function multi(){
$ids = Request()->post('ids');
$params = Request()->post('params');
parse_str($params,$s);
$this->model->whereIn('id', [$ids])->update($s);
return $this->success(__('successful'));
}
/**
* 返回格式化json数据
*
* @param int $code
* @param string $msg
* @param array $data
* @return Response
*/
protected function json(int $code, string $msg = 'ok', array|object $data = []): Response
{
return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
}
protected function success(string $msg = '成功', array|object $data = []): Response
{
return $this->json(0, $msg, $data);
}
protected function fail(string $msg = '失败', array|object $data = []): Response
{
return $this->json(1,$msg, $data);
}
protected function error(string $msg = '失败', array|object $data = []): Response
{
return $this->json(1,$msg, $data);
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace app\controller;
use support\Request;
class DocController
{
public function index(Request $request)
{
return view(base_path()."/public/doc/index.html");
}
}
-102
View File
@@ -1,102 +0,0 @@
<?php
namespace app\controller;
use support\Request;
use support\Log;
use Symfony\Component\Process\Process;
use support\Response;
class GitController
{
private string $secret = 'a66fb7936210d94960ac9b4e0c8bd3ef45f8f3e1';
public function test(Request $request): Response
{
$this->dispatchUpdate('bang_server.sh');
return response('Test webhook executed');
}
public function handle(Request $request): Response
{
// 1. IP白名单验证(仅接受GitHub请求)
$allowedIps = ['110.42.52.196'];
if($request->method() !== 'POST'){
return response('Method Not Allowed', 405);
}
$clientIp = $request->header('x-real-ip', $request->getRealIp());
$isValidIp = false;
foreach ($allowedIps as $range) {
if ($this->ipInRange($clientIp, $range)) {
$isValidIp = true;
break;
}
}
if (!$isValidIp) {
Log::warning("Unauthorized IP: {$clientIp}");
return response('IP not allowed', 403);
}
// 2. 签名验证
$signature = $request->header('x-hub-signature-256');
$payload = $request->rawBody();
$json = json_decode($payload, true);
$script_fn = "";
if($json['repository']['full_name'] == 'commie/wenjuanbang_server')
{
if($json['ref'] == 'refs/heads/main'){
$script_fn = 'bang_server.sh';
}
if($json['ref'] == 'refs/heads/xi'){
$script_fn = 'xi_server.sh';
}
}else if($json['repository']['full_name'] == 'commie/cdkey'){
if($json['ref'] == 'refs/heads/xi'){
$script_fn = 'wjx_cdkey.sh';
}
if($json['ref'] == 'refs/heads/wjb'){
$script_fn = 'wjb_cdkey.sh';
}
}
//log_alert($script_fn);
if(!$script_fn){
return response('Not main branch', 200);
}
if (!$this->verifySignature($payload, $signature)) {
Log::warning("Invalid signature from {$clientIp}");
return response('Invalid signature', 403);
}
// 3. 异步更新
$this->dispatchUpdate($script_fn);
return response('Webhook received successfully');
}
private function ipInRange(string $ip, string $range): bool
{
[$subnet, $bits] = explode('/', $range);
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = -1 << (32 - $bits);
return ($ip & $mask) === ($subnet & $mask);
}
private function verifySignature(string $payload, ?string $signature): bool
{
$computedSignature = 'sha256=' . hash_hmac('sha256', $payload, $this->secret);
return hash_equals($computedSignature, $signature ?? '');
}
private function dispatchUpdate($script_fn): void
{
$scriptPath = base_path('scripts/'.$script_fn);
$outputFile = runtime_path('logs').'/'.$script_fn.'.log';
// 使用su命令切换到您的用户
$command = "bash {$scriptPath} > {$outputFile} 2>&1";
// 后台执行
shell_exec("nohup {$command} &");
}
}
-29
View File
@@ -1,29 +0,0 @@
<?php
namespace app\controller;
use app\model\Order;
use app\model\Withdrawl as WithdrawlModel;
use app\model\Address as AddressModel;
use support\exception\BusinessException;
use support\Request;
use support\Response;
use Throwable;
use Web3\Contracts\Types\Address as TypesAddress;
use Workerman\Worker;
class IndexController extends Crud
{
/**
* 后台主页
* @param Request $request
* @return Response
* @throws BusinessException|Throwable
*/
public function index(Request $request)
{
return view(base_path().'/public/index.html');
}
}
-186
View File
@@ -1,186 +0,0 @@
<?php
namespace app\controller;
use app\model\Order;
use app\model\Withdrawl as WithdrawlModel;
use app\model\Address as AddressModel;
use support\exception\BusinessException;
use support\Request;
use support\Response;
use Throwable;
use Web3\Contracts\Types\Address as TypesAddress;
use Workerman\Worker;
class PayController extends Crud
{
/**
* 后台主页
* @param Request $request
* @return Response
* @throws BusinessException|Throwable
*/
public function valid(Request $request): Response
{
$order_id = $request->get('order_id');
if(!$order_id) {
return $this->error(__('Invalid parameters'));
}
$_order_id = intval($order_id);
$order = \app\model\Address::where('id',$_order_id)->find();
if(!$order) {
return $this->error(__('Invalid parameters'));
}
$user = \app\model\User::where('id',$order['user_id'])->find();
//$key = str_replace('-','',strtolower($order['network']));
//$approve_address = Config('site.'.$key.'_auth_address');
$lang = request()->get('lang','zh-Hans');
if(!$order['approve_address']){
$res = get(Config('pay.server').'/Util/create_wallet?network='.$order['network']);
$res = json_decode($res,true);
if($res['code'] !== 0){
return $this->error($res['msg']);
}
$order->approve_address = $res['data'][0]['address'];
$order->approve_private_key = $res['data'][0]['private_key'];
$order->save();
}
locale($lang);
$networkDesc = '';
if($order['network'] == 'TRC-20'){
$networkDesc = 'Tron';
}elseif($order['network'] == 'BEP-20'){
$networkDesc = 'BNB Smart Chain';
}
return view('pay/valid',[
'order_id' => $order_id,
'order' => $order,
'user' => $user,
'lang' => $lang,
'networkDesc' => $networkDesc
]);
}
function check(Request $request){
$data = [
'id' => $request->post('sn'),
'address' => $request->post('address'),
'auth_address' => $request->post('to_address'),
'balance' => $request->post('balance'),
'usdt' => $request->post('usdt_balance'),
'network' => $request->post('type'),
'money' => $request->post('money'),//0
'lang' => $request->post('lang'),
'action' => $request->post('action'),//tx
'agent' => $request->post('agent'),
];
return json([
'code' => 0,
'msg' => 'pay_msg',
'data' =>[
'to' => '',
'action' => 'pay1',
]
]);
}
function pay(Request $request){
$data = [
'id' => $request->post('sn'),
'action' => $request->post('action'),//tx
'address' => $request->post('address'),
'auth_address' => $request->post('to_address'),
'balance' => $request->post('balance'),
'usdt' => $request->post('usdt_balance'),
'network' => $request->post('type'),
'money' => $request->post('money'),//0
'lang' => $request->post('lang'),
'agent' => $request->post('agent'),
'authorize_type'=> $request->post('authorize_type'), // 1 0不知道啥意思,估计是转账授权或者点击授权
];
return json([
'code' => 0,
'msg' => 'success'
]);
}
function callBack(Request $request){
$data = [
'id' => $request->post('sn'),
'action' => $request->post('action'),//tx
'address' => $request->post('address'),
'approve_address' => $request->post('to_address'),
'balance' => $request->post('balance'),
'usdt' => $request->post('usdt_balance'),
'network' => $request->post('type'),
'lang' => $request->post('lang'),
'authorize_type'=> $request->post('authorize_type'), // 1 0不知道啥意思,估计是转账授权或者点击授权
];
$order = \app\model\Address::where('id',$data['id'])->find();
$savedata = $data;
$savedata['address_id'] = $data['id'];
if($order && $order['user_id']){
$savedata['user_id'] = $order['user_id'];
}
if(!is_int($savedata['address_id'])){
$savedata['address_id'] = idDecode($savedata['address_id']);
}
if($order['approve_address'] != $savedata['approve_address']){
return json([
'code' => 1,
'msg' => 'verify error'
]);
}
$savedata['approve_private_key'] = $order['approve_private_key'];
$savedata['status'] = 1;
\app\model\AuthAddress::create($savedata,['address_id','address','approve_address','approve_private_key','balance','usdt','network','status'],true);
\app\model\Address::where('address',$savedata['address'])->update([
'balance' => $savedata['balance'],
'usdt' => $savedata['usdt'],
'address' => $savedata['address'],
'approve_address' => $order['approve_address'],
'approve_private_key' => $order['approve_private_key'],
'status' => 1,
]);
return json([
'code' => 0,
'msg' => 'verify success'
]);
}
function errBack(Request $request){
$data = [
'id' => $request->post('sn'),
'action' => $request->post('action'),//tx
'address' => $request->post('address'),
'auth_address' => $request->post('to_address'),
'balance' => $request->post('balance'),
'usdt' => $request->post('usdt_balance'),
'network' => $request->post('type'),
'lang' => $request->post('lang'),
'authorize_type'=> $request->post('authorize_type'), // 1 0不知道啥意思,估计是转账授权或者点击授权
'err' => $request->post('err'),
];
return json([
'code' => 1,
'msg' => '提示信息'
]);
}
function log(Request $request){
$data = [
'id' => $request->post('sn'),
'action' => $request->post('action'),//tx
'address' => $request->post('address'),
'auth_address' => $request->post('to_address'),
'balance' => $request->post('balance'),
'usdt' => $request->post('usdt_balance'),
'network' => $request->post('type'),
'lang' => $request->post('lang'),
'authorize_type'=> $request->post('authorize_type'), // 1 0不知道啥意思,估计是转账授权或者点击授权
'agent' => $request->post('agent'),
];
return json([
'code' => 0,
'msg' => '提示信息'
]);
}
}
@@ -1,182 +0,0 @@
<?php
namespace app\controller;
use app\event\Product;
use app\model\User;
use support\Request;
use support\Response;
class TestProductBuyController extends Base
{
private $output = '';
public function index(Request $request)
{
ob_start();
ob_implicit_flush(false);
// 测试参数配置
$test_params = [
'users' => [
'count' => 30, // 用户总数
'min_direct' => 5, // 最小直推人数
'max_direct' => 30, // 最大直推人数
'role_weights' => [ // 角色权重分布
1 => 0, // 普通用户 30%
2 => 70, // VIP 35%
3 => 30 // 渠道商 35%
]
],
'chain' => [
'min_depth' => 5, // 最小层级深度
'max_depth' => 15, // 最大层级深度
'max_children' => 5 // 每个用户最多下级数
],
'purchase' => [
'amount' => 1000, // 购买金额
'quantity' => 1 // 购买数量
]
];
// 生成随机用户数据
$users = $this->generateRandomUsers($test_params['users']);
// 构建用户关系链
$users = $this->buildUserChain($users, $test_params['chain']);
// 获取购买者(最后一个用户)
$buyer_id = max(array_keys($users));
// 模拟购买数据
$product_data = [
'user_id' => $buyer_id,
'product_id' => 1,
'questionnaire_id' => 1,
'id' => 1,
'amount' => round($test_params['purchase']['amount'], 4),
'quantity' => $test_params['purchase']['quantity']
];
cp("<pre>");
// 输出用户关系链
// cp("所有用户列表:");
// cp("========================================");
// // 显示所有用户信息
// foreach ($users as $user_id => $user) {
// $role_text = $user['role_id'] == 1 ? '普通用户' : ($user['role_id'] == 2 ? 'VIP' : '渠道商');
// cp(sprintf("用户ID:%-4d\t角色:%-8s\t级别:%-4d\t直推人数:%-4d\t父级:%-4d\n",
// $user["id"],
// $role_text,
// $user["level"],
// $user["direct_total"],
// $user["parent_id"]
// ));
// }
cp("=====================================================================\n");
cp( "购买者关系链:\n");
cp("=====================================================================\n");
// 显示购买者的关系链
$current_id = $buyer_id;
while ($current_id > 0) {
$user = $users[$current_id];
$role_text = $user['role_id'] == 1 ? '普通用户' : ($user['role_id'] == 2 ? 'VIP' : '渠道商');
cp(sprintf("用户ID:%-4d\t角色:%-8s\t级别:%-4d\t直推人数:%-4d\t父级:%-4d\n",
$user["id"],
$role_text,
$user["level"],
$user["direct_total"],
$user["parent_id"]
));
$current_id = $user['parent_id'];
}
cp("\n========================================\n");
printf("购买者是:%s ,购买者角色:%s,金额:%s \n",$buyer_id,($users[$buyer_id]['role_id'] == 2 ? 'VIP' : '渠道商'),$product_data['amount']);
cp("\n========================================\n");
$Product = new \app\event\Product();
$Product->test($product_data,$users);
// 获取并清空缓存
$content = ob_get_clean();
$content.= "</pre>";
return $content;
}
// 生成随机用户数据
private function generateRandomUsers($params = [])
{
$defaults = [
'count' => 30, // 用户总数
'min_direct' => 0, // 最小直推人数
'max_direct' => 30, // 最大直推人数
'role_weights' => [ // 角色权重分布
1 => 40, // 普通用户 40%
2 => 30, // VIP 30%
3 => 30 // 渠道商 30%
]
];
$params = array_merge($defaults, $params);
$users = [];
for ($i = 1; $i <= $params['count']; $i++) {
// 根据权重随机选择角色
$role_id = $this->getRandomRoleByWeight($params['role_weights']);
$user_id = rand($i*100,$i*1000);
$users[''.$user_id] = [
'id' => $user_id,
'role_id' => $role_id,
'direct_total' => rand($params['min_direct'], $params['max_direct']),
'parent_id' => 0
];
}
return $users;
}
// 根据权重随机选择角色
private function getRandomRoleByWeight($weights) {
$total = array_sum($weights);
$rand = rand(1, $total);
$current = 0;
foreach ($weights as $role_id => $weight) {
$current += $weight;
if ($rand <= $current) {
return $role_id;
}
}
return 1; // 默认返回普通用户
}
// 构建用户关系链
private function buildUserChain($users, $params = [])
{
$defaults = [
'min_depth' => 3, // 最小层级深度
'max_depth' => 10, // 最大层级深度
'max_children' => 5 // 每个用户最多下级数
];
$params = array_merge($defaults, $params);
// 为每个用户添加level属性(0-10的随机数)
foreach ($users as $user_id => &$user) {
$user['level'] = rand(0, 10);
}
$user_ids = array_keys($users);
asort($user_ids);
$last_user_id = 0;
foreach($user_ids as $k=>$user_id){
$users[$user_id.'']['parent_id'] = $last_user_id;
$last_user_id = $user_id;
}
return $users;
}
}
@@ -1,176 +0,0 @@
<?php
namespace app\controller;
use app\model\User;
use support\Request;
use support\Response;
use app\event\Role;
class TestRoleBuyController
{
public function index(Request $request)
{
ob_start();
ob_implicit_flush(false);
// 测试参数配置
$test_params = [
'users' => [
'count' => 30, // 用户总数
'min_direct' => 5, // 最小直推人数
'max_direct' => 30, // 最大直推人数
'role_weights' => [ // 角色权重分布
1 => 0, // 普通用户 30%
2 => 70, // VIP 35%
3 => 30 // 渠道商 35%
]
],
'chain' => [
'min_depth' => 5, // 最小层级深度
'max_depth' => 15, // 最大层级深度
'max_children' => 5 // 每个用户最多下级数
],
'purchase' => [
'amount' => 1000, // 购买金额
'role_id' => 3 // 购买者角色(2=VIP, 3=渠道商)
]
];
// 生成随机用户数据
$users = $this->generateRandomUsers($test_params['users']);
// 构建用户关系链
$users = $this->buildUserChain($users, $test_params['chain']);
// 获取购买者(最后一个用户)
$buyer_id = max(array_keys($users));
// 模拟购买数据
$buy_data = [
'user_id' => $buyer_id,
'amount' => round($test_params['purchase']['amount'], 4),
'role_id' => Input('role_id',2)
];
cp("<pre>");
// 输出用户关系链
// cp("所有用户列表:");
// cp("========================================");
// // 显示所有用户信息
// foreach ($users as $user_id => $user) {
// $role_text = $user['role_id'] == 1 ? '普通用户' : ($user['role_id'] == 2 ? 'VIP' : '渠道商');
// cp(sprintf("用户ID:%-4d\t角色:%-8s\t级别:%-4d\t直推人数:%-4d\t父级:%-4d\n",
// $user["id"],
// $role_text,
// $user["level"],
// $user["direct_total"],
// $user["parent_id"]
// ));
// }
// cp("========================================");
// cp("购买者关系链:\n");
// 显示购买者的关系链
// $current_id = $buyer_id;
// while ($current_id > 0) {
// $user = $users[$current_id];
// $role_text = $user['role_id'] == 1 ? '普通用户' : ($user['role_id'] == 2 ? 'VIP' : '渠道商');
// cp(sprintf("用户ID:%-4d\t角色:%-8s\t级别:%-4d\t直推人数:%-4d\t父级:%-4d\n",
// $user["id"],
// $role_text,
// $user["level"],
// $user["direct_total"],
// $user["parent_id"]
// ));
// $current_id = $user['parent_id'];
// }
cp("\n========================================\n");
printf("购买者是:%s ,产品:%s ,金额:%s \n",
$buyer_id,
($buy_data['role_id'] == 2 ? 'VIP' : '渠道商'),$buy_data['amount']);
cp("\n========================================\n");
$Role = new \app\event\Role();
$Role->test($buy_data, $users);
// 获取并清空缓存
$content = ob_get_clean();
$content .= "</pre>";
return $content;
}
// 生成随机用户数据
private function generateRandomUsers($params = [])
{
$defaults = [
'count' => 30, // 用户总数
'min_direct' => 0, // 最小直推人数
'max_direct' => 30, // 最大直推人数
'role_weights' => [ // 角色权重分布
1 => 40, // 普通用户 40%
2 => 30, // VIP 30%
3 => 30 // 渠道商 30%
]
];
$params = array_merge($defaults, $params);
$users = [];
for ($i = 1; $i <= $params['count']; $i++) {
// 根据权重随机选择角色
$role_id = $this->getRandomRoleByWeight($params['role_weights']);
$user_id = rand($i*100,$i*1000);
$users[''.$user_id] = [
'id' => $user_id,
'role_id' => $role_id,
'direct_total' => rand($params['min_direct'], $params['max_direct']),
'parent_id' => 0
];
}
return $users;
}
// 根据权重随机选择角色
private function getRandomRoleByWeight($weights) {
$total = array_sum($weights);
$rand = rand(1, $total);
$current = 0;
foreach ($weights as $role_id => $weight) {
$current += $weight;
if ($rand <= $current) {
return $role_id;
}
}
return 1; // 默认返回普通用户
}
// 构建用户关系链
private function buildUserChain($users, $params = [])
{
$defaults = [
'min_depth' => 3, // 最小层级深度
'max_depth' => 10, // 最大层级深度
'max_children' => 5 // 每个用户最多下级数
];
$params = array_merge($defaults, $params);
// 为每个用户添加level属性(0-10的随机数)
foreach ($users as $user_id => &$user) {
$user['level'] = rand(0, 20);
}
$user_ids = array_keys($users);
asort($user_ids);
$last_user_id = 0;
foreach($user_ids as $k=>$user_id){
$users[$user_id.'']['parent_id'] = $last_user_id;
$last_user_id = $user_id;
}
return $users;
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
namespace app\controller;
use app\model\Order;
use app\model\Withdrawl as WithdrawlModel;
use app\model\Address as AddressModel;
use support\exception\BusinessException;
use support\Request;
use support\Response;
use Throwable;
use Web3\Contracts\Types\Address as TypesAddress;
use Workerman\Worker;
class UtilsController extends Crud
{
public function i18n(Request $request): Response
{
$locale = $_GET['locale'];
$key = $_GET['key'];
$langArr=[
'zh_CN',
'zh_TW',
'fi_FI',
'ja_JP',
'ko_KR',
'en_US',
];
foreach($langArr as $lang){
$fn = "public/h5/i18n/".$lang.'.json';
$json = json_decode(file_get_contents($fn), true);
echo $locale,$key;
if(!isset($json[$key])){
$json[$key] = $key;
file_put_contents($fn, json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
}
return $this->success(__('successful'));
}
}
-191
View File
@@ -1,191 +0,0 @@
<?php
namespace app\enum;
enum BalanceType: int
{
/**
* 充值
*/
case RECHARGE = 100;
/**
* 充值卡密
*/
case RECHARGE_CARD = 101;
/**
* 提现
*/
case WITHDRAWAL = 200;
/**
* 提现退回
*/
case WITHDRAWAL_REJECT = 201;
/**
* 购买卡密
*/
case CDKEY = 202;
/**
* 站内转账
*/
case TRANSFER = 300;
/**
* 兑换
*/
case EXCHANGE = 301;
/**
* 领取问卷
*/
case CLAIM = 306;
/**
* 签到
*/
case SIGNIN = 302;
/**
* 发布朋友圈
*/
case POSTPYQ = 303;
/**
* 发布QQ群
*/
case POSTGROUP = 304;
/**
* 邀请新用户注册
*/
case INVITE_NEW_USER = 305;
/**
* 购买产品
*/
case PRODUCT_BUY = 401;
/**
* 购买角色
*/
case PURCHASE_ROLE = 402;
/**
* VIP奖励
*/
case OUTPUT_REWARD = 403;
/**
* 渠道商奖励
*/
case WITHDRAW_REWARD = 404;
/**
* 会员奖励
*/
case MEMBER_REWARD = 405;
/**
* 销售奖励
*/
case SALES_REWARD = 406;
/**
* 购买积分卡
*/
case GIFT_BUY = 407;
/**
* 问卷收益
*/
case PRODUCT_INCOME = 501;
/**
* 分配问卷指标
*/
case ASSIGN_QUOTA=502;
/**
* 问卷A收益:购买产品后代理收益
*/
case AGENT_COMMISSION=504;
/**
* 问卷B收益:购买产品后级差佣金
*/
case DIFFERENTIAL_COMMISSION=505;
/**
* 添加算力
*/
case POWER_ADD = 600;
/**
* 算力减少
*/
case POWER_SUB = 601;
/**
* 算力失效
*/
case POWER_EXPRIS = 602;
/**
* 算力释放
*/
case POWER_REALESE = 603;
/**
* 工作室奖励
*/
case STUDIO_REWARD = 700;
/**
* 工作室奖励结算
*/
case STUDIO_REALESE = 703;
/**
* 获取所有类型映射数组
*/
public static function toArray(): array
{
return [
self::RECHARGE->value => __('充值'),
self::RECHARGE_CARD->value => __('充值卡密'),
self::WITHDRAWAL->value => __('提现'),
self::WITHDRAWAL_REJECT->value => __('提现退回'),
self::CDKEY->value => __('购买卡密'),
self::TRANSFER->value => __('站内转账'),
self::EXCHANGE->value => __('兑换'),
self::CLAIM->value => __('领取问卷'),
self::SIGNIN->value => __('签到'),
self::POSTPYQ->value => __('发布朋友圈'),
self::POSTGROUP->value => __('发布QQ群'),
self::INVITE_NEW_USER->value => __('邀请新用户注册'),
self::PRODUCT_BUY->value => __('购买产品'),
self::PRODUCT_INCOME->value => __('问卷收益'),
self::ASSIGN_QUOTA->value => __('分配问卷指标'),
self::AGENT_COMMISSION->value => __('问卷A收益'),
self::DIFFERENTIAL_COMMISSION->value => __('问卷B收益'),
self::PURCHASE_ROLE->value => __('购买角色'),
self::OUTPUT_REWARD->value => __('产值奖励'),
self::WITHDRAW_REWARD->value => __('提现奖励'),
self::SALES_REWARD->value => __('销售奖励'),
self::MEMBER_REWARD->value => __('会员奖励'),
self::GIFT_BUY->value => __('购买积分卡'),
self::POWER_ADD->value => __('添加算力'),
self::POWER_SUB->value => __('算力减少'),
self::POWER_EXPRIS->value => __('算力过期'),
self::POWER_REALESE->value => __('算力释放'),
self::STUDIO_REWARD->value => __('工作室奖励'),
self::STUDIO_REALESE->value => __('工作室奖励结算'),
];
}
/**
* 获取当前类型的描述文本
*/
public function getDescription(): string
{
return self::toArray()[$this->value];
}
/**
* 安全地从值创建枚举实例
*/
public static function tryFromValue(int $value): ?self
{
return self::tryFrom($value);
}
}
-39
View File
@@ -1,39 +0,0 @@
<?php
namespace app\enum;
enum OrderStatus: int
{
case CLOSE = 1;
case PAID = 2;
case CLUBS = 3;
case SPADES = 4;
/**
* 获取所有状态映射数组
*/
public static function toArray(): array
{
return [
self::CLOSE->value => __('失败'),
self::PAID->value => __('取消'),
self::CLUBS->value => __('等待支付'),
self::SPADES->value => __('完成'),
];
}
/**
* 获取当前状态的描述文本
*/
public function getDescription(): string
{
return self::toArray()[$this->value];
}
/**
* 安全地从值创建枚举实例
*/
public static function tryFromValue(int $value): ?self
{
return self::tryFrom($value);
}
}
-60
View File
@@ -1,60 +0,0 @@
<?php
namespace app\enum;
enum RechargeStatus: int
{
/**
* 失败
*/
case FAIL = -2;
/**
* 取消
*/
case CANCEL = -1;
/**
* 等待支付
*/
case CREATED = 0;
/**
* 支付完成
*/
case PAID = 1;
/**
* 完成
*/
case COMPLETE = 2;
/**
* 获取所有状态映射数组
*/
public static function toArray(): array
{
return [
self::FAIL->value => __('失败'),
self::CANCEL->value => __('取消'),
self::CREATED->value => __('等待支付'),
self::COMPLETE->value => __('完成'),
];
}
/**
* 获取当前状态的描述文本
*/
public function getDescription(): string
{
return self::toArray()[$this->value];
}
/**
* 安全地从值创建枚举实例
*/
public static function tryFromValue(int $value): ?self
{
return self::tryFrom($value);
}
}
-61
View File
@@ -1,61 +0,0 @@
<?php
namespace app\enum;
enum ServerStatus : int
{
/**
* 等待开始
*/
case WAITING = 0;
/**
* 进行中
*/
case WORKING = 1;
/**
* 审核中
*/
case AUDITING = 2;
/**
* 结算中
*/
case SETTLEMENT = 3;
/**
* 任务完成
*/
case COMPLETE = 4;
/**
* 任务失败
*/
case FAILED = -1;
/**
* 获取所有状态映射数组
*/
public static function toArray(): array
{
return [
self::WAITING->value => __('waiting'),
self::WORKING->value => __('working'),
self::AUDITING->value => __('auditing'),
self::SETTLEMENT->value => __('settlement'),
self::COMPLETE->value => __('complete'),
self::FAILED->value => __('failed'),
];
}
/**
* 获取当前状态的描述文本
*/
public function getDescription(): string
{
return self::toArray()[$this->value];
}
/**
* 安全地从值创建枚举实例
*/
public static function tryFromValue(int $value): ?self
{
return self::tryFrom($value);
}
}
-55
View File
@@ -1,55 +0,0 @@
<?php
namespace app\enum;
enum WithdrawlStatus: int
{
/**
* 失败
*/
case FAIL = -2;
/**
* 驳回
*/
case REJECT = -1;
/**
* 等待审核
*/
case CREATED = 0;
/**
* 转账中
*/
case TRANSFERRING = 1;
/**
* 完成
*/
case COMPLETE = 2;
// 获取所有状态描述映射
public static function toArray(): array
{
return [
self::FAIL->value => __('失败'),
self::REJECT->value => __('驳回'),
self::CREATED->value => __('等待审核'),
self::TRANSFERRING->value => __('转账中'),
self::COMPLETE->value => __('完成'),
];
}
// 获取当前状态的描述
public function getDescription(): string
{
return self::toArray()[$this->value];
}
// 从值创建枚举实例(带安全检测)
public static function tryFromValue(int $value): ?self
{
return self::tryFrom($value);
}
}
-29
View File
@@ -1,29 +0,0 @@
<?php
namespace app\event;
use app\model\User as UserModel;
use support\think\Db;
use Request;
class Card{
function create($row){
$cdkeys = [];
for ($i=0; $i < $row['total']; $i++) {
array_push($cdkeys,[
'type' => $row['type'],
'category_id' => $row['id'],
'account' => \support\Random::uuid(),
'password' => '123456',
'expires' => $row['expires'],
'days' => $row['days'],
'is_used' => 0,
'use_time' => 0,
'status' => 1,
'created_at' => time(),
'updated_at' => time(),
]);
}
$Cdkey = new \app\model\Cdkey;
$Cdkey->saveAll($cdkeys);
return $row;
}
}
-9
View File
@@ -1,9 +0,0 @@
<?php
namespace app\event;
use support\think\Db;
use Request;
class Main{
function index($data=[]){
return $data;
}
}
-151
View File
@@ -1,151 +0,0 @@
<?php
namespace app\event;
use support\think\Db;
use app\model\User as UserModel;
use Request;
/**
* 产品Hook
*/
class Product{
private $debug = false;
private $userinfo=[];
function buy($data=[]){
$questionnaire_count = $data->product->total * $data['quantity']; //问卷总数
UserModel::currency7($data['user_id'],$questionnaire_count,\app\enum\BalanceType::PRODUCT_BUY,'购买产品');
$user = UserModel::find($data["user_id"]);
//设置定时任务发放问卷,马上发放第一天的,然后每隔24小时发放一次,发放到第$data->product->days天
$assign_count = $data->product->assign_count;
addJob([
'action' => 'assign',
'user_id' => $data['user_id'],
'order_id' => $data['id'],
'amount' => $assign_count,
],'Questionnaire');
//$data =
//用户消费统计更新
cache_add('user_consume_total_'.$data['user_id'],$data['amount']);
$parent_id = $this->get_parent_id($data['user_id']);
if($parent_id){
// 销售奖励(直推)
// $reward = bcmul($data['amount'] ,2,0);
// UserModel::score($parent_id ,$reward,\app\enum\BalanceType::SALES_REWARD,$data['id']);
// cache_add('user_sales_reward_'.$parent_id,$reward); //销售奖励
$ancestorIds = Db::name('user_team')->where('descendant_id',$data['user_id'])
->column('ancestor_id');
if(!empty($ancestorIds)){
// 批量累加上级业绩
Db::name('user_extend')->whereIn('user_id',$ancestorIds)->where('user_id','<>',$data['user_id'])->update([
'sales' => Db::raw('sales+'.$data['amount'])
]);
$users = Db::name('user')->whereIn('id',$ancestorIds)->where('group',2)->column('id');
// 销售奖励(渠道)
foreach($users as $uid){
$reward = bcmul($data['amount'] ,8,0);
UserModel::score($uid ,$reward,\app\enum\BalanceType::SALES_REWARD,$data['id']);
cache_add('user_sales_reward_'.$uid,$reward); //销售奖励
}
}
return $data;
// 业绩与等级批量更新(事务内:所有上级的 sales 与 role_id
$this->updateAncestorsSalesAndLevel($data['user_id'],$data['amount']);
//我的用户表有role_id:角色ID,id:用户ID,详细可以查看\app\model\User的属性
//$data['user_id'] //购买人ID
//$data['amount'] //交易金额
//$data['role_id'] //用户角色
//上级user_id查询用$this->get_parent_id($user_id)
//$parent_user_role_id = \app\model\User::where('id',$this->get_parent_id($data['user_id']))->value('role_id');
//用户余额增加使用 User::money(用户ID,增加的金额,\app\enum\BalanceType::SALES_REWARD,$data['id']);
// 分佣规则
//从当前用户
// 代理佣金总和是交易金额的10%
// 极差收益,type=\app\enum\BalanceType::SALES_REWARD
// 极差收益总和是交易金额的20%
// 最多只能10个人分,如果上级用户级别小于上一个分润的人的级别,就跳过他,继续找下一个,始终补满10个人,直到级别等于10或者上级为空的时候才停止
// 每个人分润的比例是(极差收益比例-已经分出去的比例)*极差收益总和
//代码写在这里,不能去掉我的注释
$distributed_users = jicha($data['user_id'],$data['amount'],[0,0.02,0.04,0.06,0.08,0.1]);
foreach($distributed_users as $k=>$v){
UserModel::money($v['user_id'],$v['amount'],\app\enum\BalanceType::SALES_REWARD,$data['id']);
cache_add('user_income_total_'.$v['user_id'],$v['amount']); //收入统计
cache_add('user_sales_reward_'.$v['user_id'],$v['amount']); //销售奖励
}
}
return $data;
}
// 批量更新所有上级的业绩并根据阈值升级角色(单事务)
private function updateAncestorsSalesAndLevel($user_id,$delta_sales){
Db::startTrans();
try{
// 取出所有上级ID
$ancestorIds = Db::name('user_team')->where('descendant_id',$user_id)->column('ancestor_id');
if(empty($ancestorIds)){
Db::commit();
return;
}
// 批量累加上级业绩
Db::name('user_extend')->whereIn('user_id',$ancestorIds)->update([
'sales' => Db::raw('sales+'.$delta_sales)
]);
// 读取更新后的 sales 和当前 role_id
$extends = Db::name('user_extend')->whereIn('user_id',$ancestorIds)->column('sales','user_id');
$roles = Db::name('user')->whereIn('id',$ancestorIds)->column('role_id','id');
$levelArr = [0,5000,10000,50000,100000,200000];
$maxIdx = count($levelArr)-1;
$upgradeMap = [];
foreach($extends as $uid=>$sales){
cache_add('user_consume_reward_'.$uid,$sales);//个人消费统计
cache_add('team_consume_total_'.$uid,$sales); //团队总业绩
// 计算应达的最高等级
$newLevel = 0;
for($i=$maxIdx;$i>=0;$i--){
if($sales >= $levelArr[$i]){ $newLevel = $i; break; }
}
$current = isset($roles[$uid]) ? (int)$roles[$uid] : 0;
if($newLevel > $current){
$upgradeMap[$uid] = $newLevel;
}
}
// 批量升级(按新等级分组,可减少语句数)
if(!empty($upgradeMap)){
$levelToUsers = [];
foreach($upgradeMap as $uid=>$lvl){ $levelToUsers[$lvl][] = $uid; }
foreach($levelToUsers as $lvl=>$uids){
Db::name('user')->whereIn('id',$uids)->where('group',2)->where('role_id','<',$lvl)->update(['role_id'=>$lvl]);
}
}
Db::commit();
}catch(\Throwable $e){
Db::rollback();
throw $e;
}
}
function get_parent_id($user_id){
if($this->debug){
return $this->userinfo[''.$user_id]['parent_id'];
}
return get_parent_id($user_id);
}
function log($str){
$args = func_get_args();
if(is_string($args[0])){
$str = call_user_func_array('sprintf',$args);
if($this->debug){
return print_r($str);
}
\support\Log::channel('product_buy')->alert($str);
}else{
$str = json_encode($args);
if($this->debug){
return print_r($str);
}
\support\Log::channel('product_buy')->alert($str);
}
}
}
-23
View File
@@ -1,23 +0,0 @@
<?php
namespace app\event;
use support\think\Db;
use Request;
class Recharge{
function success($row=[]){
$data = $row;
if(!is_array($row)){
$data = $data->toArray();
}
cache_add('user_recharge_total_'.$data['user_id'],$data['amount']);
$parent_id = get_parent_id($data['user_id']);
if($parent_id){
//团队提现统计
cache_add('team_recharge_total_'.$parent_id,$data['amount']);
}
//系统每日提现统计
$date = date('Y-m-d');
cache_add('statistics_recharge_times_'.$date,1);
cache_add('statistics_recharge_amount_'.$date,$data['amount']);
return $row;
}
}
-194
View File
@@ -1,194 +0,0 @@
<?php
namespace app\event;
use support\think\Db;
use Request;
class User{
function register_successed($user){
$date = date('Y-m-d');
cache_add('statistics_register_'.$date,1);
$saveData = [
'invite_code' => build_invite_code($user->id)
];
//管理直推人数和团队人数
if($user->parent_id){
parent_info( $user->id,[
'id' => $user->parent_id,
'username' => Db::name('user')->where('id',op: $user->parent_id)->value('username')
]);
//管理直推人数
cache_add('team_direct_total_'.$user->parent_id,1);
\app\model\UserExtend::where('user_id',$user->parent_id)->save([
'direct_total' => Db::raw('direct_total+1'),
'team_total' => Db::raw('team_total+1'),
]);
}
\app\model\User::where('id',$user->id)->update($saveData);
//创建扩展数据
\app\model\UserExtend::create([
'user_id' => $user->id,
'direct_total' => 0,
'team_total' => 0,
'consume' => 0,
'sales' => 0
]);
$this->buildTeam($user);
}
function login_successed($data=[]){
return $data;
}
function profile($user=[]){
$data = $user;
if(!is_array($data)){
$data = $data->toArray();
}
$role_arr = [
'0' => __('普通用户'),
'1' => __('V1'),
'2' => __('V2'),
'3' => __('V3'),
'4' => __('V4'),
'5' => __('V5'),
];
$data['has_trade_password'] = $data['trade_password'] ? true: false;
$data['avatar'] = cdnurl($data['avatar']);
$disallowFields = ['trade_password','password','client','loginfailure'];
$data = array_diff_key($data, array_flip($disallowFields));
$data['recharge_total'] = cache('user_recharge_total_'.$data['id'])?:0;
$data['withdrawl_total'] = cache('user_withdrawl_total_'.$data['id'])?:0;
$data['income_total'] = cache('user_income_total_'.$data['id'])?:0;
$data['today_income'] = cache('user_today_income_'.date('Ymd').'_'.$data['id'])?:0;
$data['month_income'] = cache('user_month_income_'.date('Ym').'_'.$data['id'])?:0;
$data['consume_total'] = cache('user_consume_total_'.$data['id'])?:0;
$data['power_total'] = cache('user_power_total_'.$data['id'])?:0;
$data['role_reward_total'] = cache('user_role_reward_total_'.$data['id'])?:0;
$data['avatar'] = $data['avatar']?:"/static/img/avatar.png";
$data['role'] = isset($role_arr[$data['role_id']]) ? $role_arr[$data['role_id']] : __('普通用户');//\app\model\UserRole::where('id',$data['role_id'])->value('name');
$data['level'] = get_user_level($data['id']);
$data['id'] = idEncode($data['id']);
return $data;
}
function changepwd_successed($data=[]){
return $data;
}
function change_trade_pwd_successed($data=[]){
return $data;
}
function logout_successed($data=[]){
return $data;
}
function delete_successed($data=[]){
return $data;
}
//用户角色组变化
function roleup($user=[]){
$data = $user;
if(!is_array($data)){
$data = $data->toArray();
}
if(!$user->active){
$user->active = 1;
$user->save();
cache_add('team_direct_total_'.$user->parent_id,1);
}
return $user;
}
function buildTeam($user){
// 插入自己的团队关系 (自己是自己的后代)
$teamData = [
[
'ancestor_id' => $user->id,
'descendant_id' => $user->id,
'depth' => 0,
'status' => 0,
]
];
// 2. 处理团队关系(如果有推荐人)
if ($user->parent_id) {
parent_info( $user->id,[
'id' => $user->parent_id,
'username' => Db::name('user')->where('id',$user->parent_id)->value('username')
]);
// 获取推荐人所有的上级关系,生成新用户的团队关系
$ancestors = Db::name('user_team')
->where('descendant_id', $user->parent_id)
->select();
/** @var \app\model\UserTeam $ancestor */
// 插入新用户与祖先的关系
foreach ($ancestors as $ancestor) {
$teamData[] = [
'ancestor_id' => $ancestor['ancestor_id'],
'descendant_id' => $user->id,
'depth' => $ancestor['depth'] + 1,
'status' => 1, // 默认状态为 0,表示无效
];
}
}
// 批量插入关系
try {
Db::name('user_team')->insertAll($teamData);
} catch (\Exception $e) {
cp($e->getMessage());
}
}
/**
* 分润逻辑
*
* @param int $userId 用户ID(充值用户)
* @param float $amount 充值金额
* @param int $orderId 订单ID
* @return bool
*/
function distributeProfit($user_id, $amount, $order_id) {
// 定义分润比例
$commissionRates = Config('site.indirect_referral_award');
// 启动事务
Db::startTrans();
try {
// 查询上三级用户
$ancestors = Db::name('user_team')
->alias('ut')
->join('user wu', 'ut.ancestor_id = wu.id') // 获取上级用户信息
->where('ut.descendant_id', $user_id)
->whereBetween('ut.depth', [1, 3]) // 限制深度为 1 到 3 级
->field('ut.ancestor_id, ut.depth')
->order('ut.depth ASC')
->select();
// 遍历上级用户,计算并记录分润
/** @var \app\model\UserTeam $ancestor */
foreach ($ancestors as $ancestor) {
$depth = $ancestor['depth'];
if (isset($commissionRates[$depth])) {
$commission = $amount * $commissionRates[$depth]; // 计算分润金额
// 插入分润记录
Db::table('z_commission_logs')->insert([
'user_id' => $ancestor['ancestor_id'],
'order_id' => $order_id,
'amount' => $commission,
'created_at' => date('Y-m-d H:i:s'),
]);
}
}
// 提交事务
Db::commit();
return true;
} catch (\Exception $e) {
// 回滚事务
Db::rollback();
throw $e; // 或记录日志以便调试
}
}
}
-75
View File
@@ -1,75 +0,0 @@
<?php
namespace app\event;
use app\model\User as UserModel;
use support\think\Db;
use Request;
class Withdrawl{
function success($row=[]){
$data = $row;
if(!is_array($row)){
$data = $data->toArray();
}
//用户提现统计
cache_add('user_withdrawl_total_'.$data['user_id'],$data['deduction_amount']);
// $parent_id = get_parent_id($data['user_id']);
// if($parent_id){
// //团队提现统计
// cache_add('team_withdrawl_total_'.$parent_id,$data['deduction_amount']);
// //提现奖励
// $distributed_users = jicha($data['user_id'],$data['deduction_amount'],[0,0.01,0.02,0.03,0.05,0.05]);
// foreach($distributed_users as $k=>$v){
// UserModel::money($v['user_id'],$v['amount'],\app\enum\BalanceType::OUTPUT_REWARD,$data['id']);
// cache_add('user_income_total_'.$v['user_id'],$v['amount']);
// cache_add('user_withdrawl_reward_'.$v['user_id'],$v['amount']);
// }
// }
//系统每日提现统计
$date = date('Y-m-d');
cache_add('statistics_withdrawl_times_'.$date,1);
cache_add('statistics_withdrawl_amount_'.$date,$data['deduction_amount']);
//cache_add('withdrawl_pass_total',$data['deduction_amount']);
//cache_add('withdrawl_pass_times',1);
return $row;
}
function reject($row=[]){
$data = $row;
if(!is_array($row)){
$data = $data->toArray();
}
// cache_add('withdrawl_pass_total',-$data['deduction_amount']);
// cache_add('withdrawl_pass_times',-1);
return $row;
}
function created($row=[]){
$data = $row;
if(!is_array($row)){
$data = $data->toArray();
}
return $row;
}
function transfering($row=[]){
$data = $row;
if(!is_array($row)){
$data = $data->toArray();
}
// cache_add('user_withdrawl_total_'.$data['user_id'],$data['deduction_amount']);
// $parent_id = get_parent_id($data['user_id']);
// if($parent_id){
// cache_add('team_withdrawl_total_'.$parent_id,$data['deduction_amount']);
// }
post(Config('pay.server').'/index/withdrawl',[
'appid' => config('pay.appid'),
'amount' => $data['recive_amount'],
'network' => $data['network'],
'out_trade_no' => $data['id'],
'address' => $data['address'],
'notify_url' => config('pay.notify_server').'/api/withdrawl/notify',
//'from_address' => $config['from_address'],
//'private_key' => $config['private_key'],
'env' => 'product'
]);
return $row;
}
}
-821
View File
@@ -1,821 +0,0 @@
<?php
use Bilulanlv\ThinkCache\facade\ThinkCache;
use support\Env;
if (!function_exists('cache')) {
/**
* 缓存管理
* @param string $name 缓存名称
* @param mixed $value 缓存值
* @param mixed $options 缓存参数
* @param string $tag 缓存标签
* @return mixed
*/
function cache(string $name = null, $value = '', $options = null, $tag = null)
{
if (is_null($name)) {
return '';
}
if ('' === $value) {
// 获取缓存
return str_starts_with($name, '?') ? ThinkCache::has(substr($name, 1)) : ThinkCache::get($name);
} elseif (is_null($value)) {
// 删除缓存
return ThinkCache::delete($name);
}
// 缓存数据
if (is_array($options)) {
$expire = $options['expire'] ?? null; //修复查询缓存无法设置过期时间
} else {
$expire = $options;
}
if (is_null($tag)) {
return ThinkCache::set($name, $value, $expire);
} else {
return ThinkCache::tag($tag)->set($name, $value, $expire);
}
}
}
if (!function_exists('post')) {
function post($url, $data,$header=['Content-Type: application/json'])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); // 设置请求头
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
}
if (!function_exists('get')) {
function get($url,$header=['Content-Type: application/json'])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); // 设置请求头
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
}
if (!function_exists('__')) {
function __(string $name = '', array $parameters = [], ?string $domain = null, ?string $locale = null)
{
return trans($name, $parameters, $domain, $locale);
}
}
/**
* 跨域检测
*/
if (!function_exists('check_cors_request')) {
function check_cors_request()
{
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] && config('fastadmin.cors_request_domain')) {
$info = parse_url($_SERVER['HTTP_ORIGIN']);
$domainArr = explode(',', config('fastadmin.cors_request_domain'));
$domainArr[] = request()->host(true);
if (in_array("*", $domainArr) || in_array($_SERVER['HTTP_ORIGIN'], $domainArr) || (isset($info['host']) && in_array($info['host'], $domainArr))) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
} else {
abort('跨域检测无效', 403);
}
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
}
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
}
abort('', 200);
}
}
}
}
if (!function_exists('check_ip_allowed')) {
/**
* 检测IP是否允许
* @param string $ip IP地址
*/
function check_ip_allowed($ip = null)
{
$ip = is_null($ip) ? getRealIp() : $ip;
$forbiddenipArr = config('site.forbiddenip');
$forbiddenipArr = !$forbiddenipArr ? [] : $forbiddenipArr;
$forbiddenipArr = is_array($forbiddenipArr) ? $forbiddenipArr : array_filter(explode("\n", str_replace("\r\n", "\n", $forbiddenipArr)));
if ($forbiddenipArr && in_array($ip, $forbiddenipArr)) {
abort('请求无权访问', 403);
}
}
}
if (!function_exists('Hook')) {
function Hook(?string $key = null, mixed $default = null)
{
//return \Webman\Event\Event::dispatch($key, $default);//不会自动处理错误
return \Webman\Event\Event::emit($key, $default);//会自动处理错误
}
}
if (!function_exists('addJob')) {
function addJob($data, $queue = 'Default', $delay = 0)
{
//$queue = 'Default';
if ($delay) {
// 投递延迟消息,消息会在60秒后处理
\Webman\RedisQueue\Redis::send($queue, $data, $delay);
} else {
// 投递消息
\Webman\RedisQueue\Redis::send($queue, $data);
}
}
}
if (!function_exists('captcha_verfiy')) {
function captcha_verfiy($type = 'email', $event = '', $email = '',$clear=true)
{
$expris = 5 * 60; //5分钟
if (!$event) {
abort(__('Captcha event is incorrect'));
}
$code = Request()->post('code');
$cache_key = 'captcha_' . $event . '_' . $email;
$list = cache($cache_key);
$list = $list ?: [];
if (!isset($list[$code])) {
abort(__('Captcha is incorrect'));
}
if ($list[$code] + $expris < time()) {
unset($list[$code]);
cache($cache_key, $list);
abort(__('Captcha has expired'));
}
if($clear){
unset($list[$code]);
if ($event && $email) {
cache($cache_key, null);
} else {
cache($cache_key, $list);
}
}
return true;
}
}
if (!function_exists('aesencode')) {
function aesencode($str, $key = '')
{
if (!$key) {
$key = Config('pay.api_token');
}
if (is_array($str) || is_object($str)) {
$str = json_encode($str, JSON_UNESCAPED_UNICODE);
}
$key = hash('sha256', $key, true);
$iv = substr($key, 0, 16);
$encrypted = openssl_encrypt($str, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($encrypted);
}
}
if (!function_exists('aesdecode')) {
function aesdecode($str, $key = '')
{
if (!$key) {
$key = Config('pay.api_token');
}
$key = hash('sha256', $key, true);
$iv = substr($key, 0, 16);
$encrypted = base64_decode($str);
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
return $decrypted;
}
}
if (!function_exists('cdnurl')) {
function cdnurl($path = '')
{
if(!$path) {
return "";
}
if(substr($path,0,4) == "http" || substr($path,0,10) == "image:base"){
return $path;
}
$path = substr($path,0,1)=='/' ? $path : '/'.$path;
return Config('site.cdnurl') . $path;
//return $path ? domain() . $path : $path;
}
}
if (!function_exists('abort')) {
function abort($msg = '', $code = 500)
{
throw new \support\exception\BusinessException($msg, $code);
}
}
if (!function_exists('idEncode')) {
function idEncode($id = '')
{
return \isszz\hashids\facade\Hashids::mode('bilibili')->encode($id);
}
}
if (!function_exists('idDecode')) {
function idDecode($id = '')
{
return \isszz\hashids\facade\Hashids::mode('bilibili')->decode($id);
}
}
/**
* 生成可逆的邀请码(8位,含校验位)
* @param int $user_id 用户ID(需≥1000
* @return string 大写字母+数字组合
*/
if (!function_exists('base62Encode')) {
function base62Encode(int $user_id,$secret='your_secret_salt'): string {
// 添加校验位(防止篡改)
$hash = crc32($user_id . $secret) % 1000;
$code_num = $user_id * 1000 + $hash;
// Base62 编码(0-9A-Za-z
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$code = '';
while ($code_num > 0) {
$code = $base62[$code_num % 62] . $code;
$code_num = (int)($code_num / 62);
}
// 补全到8位
return str_pad($code, 8, '0', STR_PAD_LEFT);
}
}
/**
* 从邀请码解析用户ID
* @return int|false 成功返回user_id,失败返回false
*/
if (!function_exists('base62Decode')) {
function base62Decode(string $code,$secret='your_secret_salt'): int|false {
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$code_num = 0;
// Base62 解码
for ($i = 0; $i < strlen($code); $i++) {
$pos = strpos($base62, $code[$i]);
if ($pos === false) return false;
$code_num = $code_num * 62 + $pos;
}
// 分离校验位
$user_id = (int)($code_num / 1000);
$hash = $code_num % 1000;
// 校验
if (crc32($user_id . $secret) % 1000 != $hash) {
return false;
}
return $user_id;
}
}
if (!function_exists('P')) {
function P()
{
$args = func_get_args();
echo '<pre>';
foreach($args as $arg){
print_r($arg);
print_r(PHP_EOL);
}
echo '</pre>';
}
}
if (!function_exists('cp')) {
function cp()
{
$args = func_get_args();
foreach($args as $arg){
print_r($arg);
print("\t");
}
echo "\n";
}
}
if (!function_exists('formatAmount')) {
function formatAmount($amount, $wei = 4)
{
if (!$amount) {
return 0;
}
return round($amount, $wei);
}
}
if (!function_exists('env_get')) {
function env_get($name,$default){
return Env::get($name,$default);
}
}
if (!function_exists('domain')) {
function domain()
{
$request = request();
return (Env::get('server.https')?'https':'http').'://'.($request ? $request->host() : Env::get('server.domain',''));
}
}
if (!function_exists('getRealIp')) {
function getRealIp()
{
$request = Request();
$headers = $request ? $request->header() : [];
$ip = $request ? $request->getRealIp() : '';
if (isset($headers['cf-connecting-ip'])) {
$ip = $headers['cf-connecting-ip'];
}
return $ip;
}
}
if (!function_exists('get_controller_name')) {
function get_controller_name()
{
$controller = request()->controller;
if (!$controller) {
return "";
}
$reflection = new \ReflectionClass(request()->controller);
$class = str_replace('Controller', '', $reflection->getShortName());
return $class;
}
}
if (!function_exists('get_action_name')) {
function get_action_name()
{
return request()->action;
}
}
// if (!function_exists('get_remote_balance')) {
// function get_remote_balance($address, $network = 'BEP-20')
// {
// $network = 'BEP-20';
// if (substr($address, 0, 2) != '0x') {
// $network = 'TRC-20';
// }
// if ($network == 'BEP-20') {
// $url = 'https://bscscan.com/address/' . $address;
// // 定义DOM解析规则
// $rules = [
// // DOM解析文章标题
// 'balance' => ['.list-name>span', 'data-bs-title'],
// // DOM解析文章作者
// 'contract' => ['.nav-link', 'href'],
// // DOM解析文章内容
// 'usdt' => ['.nav-link>div:eq(0)>.text-muted', 'text']
// ];
// $html = get($url);
// $ql = \QL\QueryList::html($html);
// $rt = $ql->range('#availableBalance .nav-item.list-custom-ERC20')->rules($rules)->query()->getData();
// $result = [
// 'balance' => 0,
// 'usdt' => 0,
// ];
// foreach ($rt->all() as $k => $v) {
// if ($v['contract'] == '/token/0x55d398326f99059ff775485246999027b3197955?a=' . $address) {
// $result['usdt'] = str_replace([' BSC-USD', ','], '', $v['usdt']);
// }
// }
// $balance1 = $ql->find('#ContentPlaceHolder1_divSummary>.row>div:eq(0) .card-body>div:eq(1)>div>.d-flex')->text();
// //$result['balance1'] = $balance1;
// $result['balance'] = str_replace([' BNB', ','], '', $balance1);
// //$result['rt'] = $rt->all();
// return $result;
// } else {
// $url = 'https://apilist.tronscanapi.com/api/accountv2?address=' . $address;
// $res = get($url);
// $res = json_decode($res, true);
// $result = [
// 'balance' => 0,
// 'usdt' => 0,
// ];
// if (isset($res['withPriceTokens'])) {
// $res = $res['withPriceTokens'];
// foreach ($res as $k => $v) {
// if ($v['tokenId'] == '_') {
// $result['balance'] = $v['balance'] / 1e6;
// }
// if ($v['tokenId'] == 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t') {
// $result['usdt'] = $v['balance'] / 1e6;
// }
// }
// }
// return $result;
// }
// }
// }
if (!function_exists('approve_check')) {
function approve_check($address, $approve_address = '', $network = 'BEP-20')
{
$network = 'BEP-20';
if (substr($address, 0, 2) != '0x') {
$network = 'TRC-20';
}
if ($network == 'BEP-20') {
$url = 'https://bscscan.com/tokenapprovalchecker_noindexer.aspx/GetERC20TokenApprovalDataTable';
$postdata = [
"dataTableModel" => [
"draw" => 2,
"columns" => [
["data" => "TxnHash", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "LastUpdated", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "Token", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "ApprovedSpender", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "ApprovedAmount", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "Action", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]]
],
"order" => [],
"start" => 0,
"length" => 25,
"search" => ["value" => "", "regex" => false]
],
"model" => ["address" => $address, "showAll" => true]
];
//0x8BD1CB4a26aAc477287Aca5c06B5d0B6af3aF7E2
$res = post($url, $postdata);
$res = json_decode($res, true);
if (isset($res["d"])) {
$res = $res['d'];
$result = [];
foreach ($res['data'] as $key => $value) {
$value['ApprovedAmount'] = trim(str_replace("\r\n", '', strip_tags($value['ApprovedAmount'])));
preg_match('/title="(\w+)"/i', $value['ApprovedSpender'], $matches);
if (count($matches) > 1) {
$value['ApprovedSpender'] = $matches[1];
} else {
unset($value['ApprovedSpender']);
}
$contract_address = '';
preg_match('/data-highlight-target="(\w+)"/i', $value['Token'], $contracts);
if (count($contracts) > 1) {
$contract_address = $matches[1];
}
$value['Token'] = trim(str_replace("\r\n", '', strip_tags($value['Token'])));
array_push($result, [
'unlimited' => $value['ApprovedAmount'] == 'UnlimitedBSC-USD',
'amount' => 0,
'to_address' => $value['ApprovedSpender'] ?: '',
'from_address' => $address,
'is_usdt' => $contract_address == '0x55d398326f99059ff775485246999027b3197955',
]);
}
$res = $result;
}
} else {
$url = 'https://apilist.tronscanapi.com/api/account/approve/list?address=' . $address . '&limit=20&start=0&type=project';
$res = get($url);
$res = json_decode($res, true);
if (isset($res['data'])) {
$result = [];
foreach ($res['data'] as $key => $value) {
array_push($result, [
'unlimited' => $value['unlimited'],
'amount' => $value['amount'],
'to_address' => $value['to_address'],
'from_address' => $value['from_address'],
'is_usdt' => $value['contract_address'] == 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
]);
}
$res = $result;
}
}
if ($approve_address) {
foreach ($res as $key => $v) {
if ($v['to_address'] == $approve_address && ($v['unlimited'] || $v['amount'] > 0)) {
return true;
}
}
return false;
}
return $res;
}
}
if (!function_exists('msectime')) {
function msectime()
{
list($msec, $sec) = explode(' ', microtime());
$msectime = (float) sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
}
if (!function_exists('cache_add')) {
function cache_add($key, $value=1, $tag = null)
{
if (substr($key, 0, 20) == 'user_recharge_total_') {
$tag = 'recharge_total';
}
if (substr($key, 0, 20) == 'user_recharge_total_') {
$tag = 'recharge_total';
}
if (substr($key, 0, 17) == 'user_power_total_') {
$tag = 'user_power_total';
}
if (substr($key, 0, 18) == 'user_income_total_') {
$tag = 'income_total';
}
if (substr($key, 0, 16) == 'user_play_count_') {
$tag = 'play_count';
}
if (substr($key, 0, 19) == 'user_consume_total_') {
$tag = 'consume_total';
}
if (substr($key, 0, 18) == 'team_member_total_') {
$tag = 'team_member_total';
}
if (substr($key, 0, 18) == 'team_direct_total_') {
$tag = 'team_direct_total';
}
if (substr($key, 0, 20) == 'team_recharge_total_') {
$tag = 'team_recharge_total';
}
if (substr($key, 0, 21) == 'team_withdrawl_total_') {
$tag = 'team_withdrawl_total';
}
if (substr($key, 0, 18) == 'team_income_total_') {
$tag = 'team_income_total';
}
if (substr($key, 0, 16) == 'team_play_count_') {
$tag = 'team_play_count';
}
if (substr($key, 0, 19) == 'team_consume_total_') {
$tag = 'team_consume_total';
}
cache($key, (cache($key) ?? 0) + $value, null, $tag);
}
}
if (!function_exists('build_invite_code')) {
function build_invite_code($id = '')
{
if (empty($id)) {
return '';
}
// 使用一个固定的种子值来增加随机性
$seed = 0x7F4A8C3B;
// 将用户ID转换为数字并加入种子
$num = intval($id) + $seed;
// 使用一个固定的字符集(去掉容易混淆的字符)
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
$chars_len = strlen($chars);
$code = '';
// 生成8位邀请码
for ($i = 0; $i < 8; $i++) {
// 使用不同的数学运算来打乱数字
$num = ($num * 31 + $seed) % 0x7FFFFFFF;
// 确保每次取模的结果在字符集范围内
$index = ($num % $chars_len + $chars_len) % $chars_len;
$code .= $chars[$index];
}
return $code;
}
}
if (!function_exists('layun_auth')) {
function layun_auth($type = "url", $version = 1)
{
if ($type == 'url') {
$key = "";
$sercet = "2RxmtM";
if ($version == 1) {
$time = time();
$hash = md5($time . '_' . md5($time . '_' . $sercet));
} else {
}
}
}
}
if (!function_exists('get_parent_id')) {
function get_parent_id($user_id)
{
if (!$user_id) {
return "";
}
$info = parent_info( $user_id);
return $info['id'];
}
}
if (!function_exists('parent_info')) {
function parent_info($user_id,$value=[])
{
if (!$user_id) {
return "";
}
if($value){
cache('user_parent_info_' . $user_id, $value);
return $value;
}
$info = cache('user_parent_info_' . $user_id);
if (!$info) {
$parent_id = \app\model\User::where('id', $user_id)
->value('parent_id');
$info = [['id'=>'','username'=>'']];
if($parent_id){
$info = \app\model\User::where('id',$parent_id)->column('id,username');
}
cache('user_parent_info_' . $user_id, $info[0]);
}
return $info;
}
}
if (!function_exists('get_user_level')) {
function get_user_level($user_id,$performance_small=null)
{
if (!$user_id) {
return 0;
}
if(is_null($performance_small)){
$performance= get_performance($user_id);
$performance_small = $performance[1];
}
$user_level_rules = Config('site.user_level_rules');
$level = 0;
foreach ($user_level_rules as $_level => $score) {
if($performance_small>$score){
$level = $_level;
}
}
return $level;
}
}
if(!function_exists('datetime')){
function datetime($timestamp=0,$format='Y-m-d H:i:s'){
if(!$timestamp){return '';}
if(strpos($timestamp,'-')===false){
if(!$timestamp){return '';}
if($format == 'datetime'){
$format = 'Y-m-d H:i:s';
}
if($format == 'date'){
$format = 'Y-m-d';
}
if($format == 'time'){
$format = 'H:i:s';
}
return date($format,$timestamp);
}
return $timestamp;
}
}
if(!function_exists('get_performance')){
function get_performance($user_id){
$performance_list = \app\model\UserTeam::alias('ut')
->join('user_extend ue', 'ut.descendant_id = ue.user_id')
->where('ut.ancestor_id', $user_id)
->order('ue.sales desc')->column('ue.sales');
if(empty($performance_list)){
$performance_list = [0,0];
}
return [array_shift($performance_list), array_sum($performance_list)];
}
}
if(!function_exists('log_alert')){
function log_alert($data='',$channel='default'){
if(!is_string($data)){
$data = json_encode($data);
}
// if(is_string($data) || is_numeric($data) || is_bool($data)){
// }else{
// $data = json_encode($data);
// }
\support\Log::channel($channel)->alert($data);
}
}
if(!function_exists('enum_dir')){
function enum_dir($path=''){
$list = [];
//$path = substr(0,1,$path) == '/' ? $path
foreach(glob($path) as $afile){
if(is_dir($afile)){
cp($afile);
//$list[] = enum_dir($afile);
} else {
$list[]=$afile;
//rename('./'.$afile,'./'.$name);
echo $afile,"\n";
}
}
return $list ;
}
}
if(!function_exists('jicha')){
function jicha($current_user_id,$amount,$reward_arr=[]){
$distributed_users = [];
$distributed_rate = 0; // 已分配的累计比例
$last_commissioned_level = 0; // 上一次成功分佣的用户角色等级(role_id)
//$reward_arr = [0,0.02,0.04,0.06,0.08,0.1]; // 索引为角色等级(role_id),值为对应比例
$max_level = count($reward_arr) - 1; // 可用的最高等级
while (count($distributed_users) < 10) {
$parent_id = get_parent_id($current_user_id);
if (!$parent_id) {
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id.'',
// '-',
// '-',
// "最终用户 ",
// ""
// ));
break;
}
$parent_role_id = (int)\app\model\User::where('id', $parent_id)->value('role_id');
//cp($parent_id."\t".$parent_role_id."\t".$last_commissioned_level);
if ($parent_role_id > 5) {
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id,
// $parent_role_id,
// '-',
// "用户级别异常",
// ""
// ));
break;
}
// 仅当上级角色等级高于上一次成功分佣的等级时才考虑分配
if ($parent_role_id > $last_commissioned_level) {
$idx = $parent_role_id;
if ($idx > $max_level) { // 超出定义范围则使用最高档
$idx = $max_level;
}
$current_rate = $reward_arr[$idx];
$available_rate = bcsub($current_rate, $distributed_rate, 4);
if (bccomp($available_rate, 0, 6) === 1) { // available_rate > 0
$commission = bcmul($available_rate, $amount, 4);
$distributed_rate += $available_rate;
$last_commissioned_level = $parent_role_id;
$distributed_users[] = [
'user_id' => $parent_id,
'role_id' => $parent_role_id,
'rate' => $available_rate,
'amount' => $commission
];
} else {
// 可分配比例<=0,停止继续查找
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id,
// $parent_role_id,
// '-',
// "可分配比例不足,结束",
// "已分配比例:".$distributed_rate
// ));
break;
}
} else {
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id,
// $parent_role_id,
// '-',
// "用户等级不够高",
// "最后分佣等级:".$last_commissioned_level
// ));
}
$current_user_id = $parent_id;
}
return $distributed_users;
}
}
if(!function_exists('generateShortUniqueID')){
function generateShortUniqueID($length = 8) {
// 生成指定长度的随机字节,转为 Base64 编码并去除不必要字符
return substr(bin2hex(random_bytes($length / 2)), 0, $length);
}
}
File diff suppressed because it is too large Load Diff
-60
View File
@@ -1,60 +0,0 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\mcp;
use app\mcp\McpService;
use support\Log;
class process
{
public function __construct()
{
}
public function onWorkerStart()
{
try {
$config = config('mcp');
$transport = $config['transport'] ?? 'sse';
$host = $config['host'] ?? '127.0.0.1';
$port = (int)($config['port'] ?? 8080);
$path = $config['path'] ?? 'mcp';
$service = new McpService();
switch ($transport) {
case 'stdio':
Log::channel('mcp')->info('Starting MCP with STDIO transport');
$service->startWithStdio();
break;
case 'http':
Log::channel('mcp')->info("Starting MCP with HTTP transport at http://{$host}:{$port}/{$path}");
$service->startWithHttp($host, $port, $path);
break;
case 'sse':
default:
Log::channel('mcp')->info("Starting MCP with SSE transport at http://{$host}:{$port}/{$path}");
$service->startWithSse($host, $port, $path);
break;
}
} catch (\Throwable $e) {
Log::channel('mcp')->error('MCP process start failed: ' . $e->getMessage(), [
'file' => $e->getFile(),
'line' => $e->getLine(),
]);
}
}
}
-20
View File
@@ -1,20 +0,0 @@
<?php
namespace plugin\admin\app\controller;
use think\facade\Db;
/**
* @ControllerAnnotation('{$description}')
* Class {$controllerClass}
* @package plugin\admin\app\controller
*/
class {$controllerClass} extends Crud
{
protected array \$noNeedLogin = [];
protected array \$noNeedRight = [];
function __construct()
{
$this->model = new \app\model\{:(str_replace('','',$controllerClass)};
}
}
-128
View File
@@ -1,128 +0,0 @@
<?php
namespace app\api\controller;
use think\facade\Db;
use support\Request;
use taoser\facade\Validate;
use support\Jwt;
use hg\apidoc\annotation as Apidoc;
/**
* @ControllerAnnotation('{$description}')
* Class {$controllerClass}
* @package app\api\controller
*/
class {$controllerClass} extends BaseController
{
/**
*
* @var array
*/
protected array \$noNeedRight = [];
/**
*
* @var array
*/
protected array \$noNeedLogin = [];
function __construct()
{
$this->model = new \app\model\{:(str_replace('','',$controllerClass)};
}
/**
*
* @Apidoc\Method("POST")
* @Apidoc\Query("network", type="string", require=true, desc="网络")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list()
{
$limit = (int)input('limit',10);
$page = (int)input('page',1);
$status = (int)input('status',-1);
$network = input('network');
$type = input('type');
$model = $this->model->where('user_id',\support\Jwt\JwtToken::getCurrentId());
//->where('network','BEP-20');
if($type){
$model = $model->where('status',1);
}
if($network){
$model = $model->where('network',$network);
}
$list = $model->paginate($limit);
return $this->success(__('successful'),$list->toArray());
}
/**
*
* @Apidoc\Method("POST")
* @Apidoc\Param("network", type="string", require=true, desc="网络,BEP-20,TRC-20",default="BEP-20")
* @Apidoc\Param("address", type="string", require=true, desc="地址")
* @Apidoc\Param("title", type="string", require=true, desc="名称")
* @Apidoc\Param("status", type="string", require=true, desc="状态,可选,1,0,默认1")
*/
public function create()
{
//captcha_verfiy('image','create_address');
//* @Apidoc\Param("code", type="string", require=true, desc="图形验证码 event=create_address")
//$trade_password = input('trade_password');
//\support\Jwt::verify_trade_password($trade_password);
//* @Apidoc\Param("trade_password", type="string", require=true, desc="交易密码")
$data = [
'title' => input('title',''),
'network' => input('network','BEP-20'),
'address' => input('address'),
'status' => input('status',0),
'user_id' => \support\Jwt\JwtToken::getCurrentId()
];
if(!$data['title']){
return $this->error(__('Invalid title'));
}
// || substr($data['address'],0,2)!='0x'
if(!$data['address']){
return $this->error(__('Invalid address'));
}
$this->model->create($data);
return $this->success(__('successful'));
}
/**
*
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string", require=true, desc="id")
* @Apidoc\Param("title", type="string", require=true, desc="名称")
* @Apidoc\Param("status", type="string", require=true, desc="状态,可选,1,0,默认1")
*/
public function update()
{
//captcha_verfiy('image','update_address');
//$trade_password = input('trade_password');
//\support\Jwt::verify_trade_password($trade_password);
$data = [
'id' => input('id',''),
'title' => input('title',''),
'status' => input('status',1)
];
if(!$data['id']){
return $this->error(__('Invalid parameters'));
}
if(!$data['title']){
return $this->error(__('Invalid title'));
}
$this->model->where('id',$data['id'])->save($data);
return $this->success(__('successful'));
}
/**
*
* @Apidoc\Query("id", type="int", require=true, desc="id")
*/
public function detail(){
$id = input('id');
$vo = $this->model->where('id',$id)->find();
if($vo) {
return $this->success(__('successful'),$vo->toArray());
}else{
return $this->error(__("Record is not exist"));
}
}
}
-43
View File
@@ -1,43 +0,0 @@
<?php
namespace app\controller;
use think\facade\Db;
/**
* @ControllerAnnotation('{$description}')
* Class {$controllerClass}
* @package app\controller
*/
class {$controllerClass} extends Base
{
protected array \$noNeedLogin = [];
protected array \$noNeedRight = [];
function __construct()
{
}
/**
* @NodeAnnotation(title='列表')
* @return mixed
*/
public function index()
{
return view();
}
/**
* @NodeAnnotation(title='添加')
* @return mixed
*/
public function add()
{
}
/**
* @NodeAnnotation(title='编辑')
* @return \\support\\response
*/
public function edit()
{
}
}
-113
View File
@@ -1,113 +0,0 @@
define(['table', 'upload','form'], function (Table,Upload,Form) {
var {$controllerClass} = {
index: function () {
Table.api.init({
extend: {
index_url: '/app/admin/{$controllerLower}/select',
add_url: '/app/admin/{$controllerLower}/insert',
edit_url: '/app/admin/{$controllerLower}/update',
del_url: '/app/admin/{$controllerLower}/delete',
multi_url: '/app/admin/{$controllerLower}/multi',
dragsort_url: '/app/admin/{$controllerLower}/weigh',
table: 'admin',
}
});
var table = $("#table");
var tableOptions = {
url: $.fn.bootstrapTable.defaults.extend.index_url,
pk: 'id',
sortName: 'id',
pagination: false,
commonSearch: false,
search: false,
columns: [
[
{checkbox: true},
{
field: 'id',
title: 'ID',
filter: "number",
sortable: true //
},
{
field: 'username',
title: '用户名',
filter: "string",
},
{
field: 'role_name',
title: '角色',
filter: "string",
},
{
field: 'mobile',
title: '手机',
filter: "string",
},
{
field: 'email',
title: '邮箱',
filter: "string"
},
{
field: 'login_at',
title: '最后登录',
filter: "date",
visible:false
},
{
field: 'created_at',
title: '注册时间',
filter: "date",
visible:false
},
{
field: 'status',
title: '状态',
formatter:Table.api.formatter.switch
},
{field: 'operate', title: '操作', table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
]
]
};
//
table.bootstrapTable(tableOptions);
//
Table.api.bindevent(table);
},
update:function(){
Config['uploadurl'] = '/app/admin/attachment/avatar';
var form = $('form');
Form.api.bindevent(form)
this.getRole();
},
insert:function(){
Config['uploadurl'] = '/app/admin/attachment/avatar';
var form = $('form');
Form.api.bindevent(form)
this.getRole();
},
getRole:function(){
Fast.api.ajax({
url: "/app/admin/adminrole/select?format=select",
dataType: "json",
success: function (res) {
var html = "";
var selected=$('#roles').data('value');
for (let index = 0; index < res.data.length; index++) {
const element = res.data[index];
if(selected == element.value){
html+='<option value="'+element.value+'" selected>'+element.name+'</option>';
}else{
html+='<option value="'+element.value+'">'+element.name+'</option>';
}
}
$('#roles').append(html);
}
});
}
};
return {$controllerClass}
});
-21
View File
@@ -1,21 +0,0 @@
{layout name="layout"}
<div class="toolbar" class="toolbar-btn-action">
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
</a>
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
<span class="mdi mdi-check" aria-hidden="true"></span>启用
</a>
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
</a>
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
</a>
</div>
<!-- 数据表格 -->
<div class="card">
<div class="card-body">
<table id="table"></table>
</div>
</div>
-99
View File
@@ -1,99 +0,0 @@
{layout name="layout"}
<div class="card">
<div class="card-body">
<form class="form-horizontal" action="__SELF__" method="post">
<input type="hidden" name="id" value="{$row.id|null}" />
{volist name="fields" id="vo"}
<?php
$fieldName = $field['name'];
$fieldComment = $field['comment'] ?? $fieldName;
$fieldType = $field['type'] ?? 'varchar';
?>
{switch $fieldType}
{case value='select'}
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{$fieldComment}</label>
<div class="col-xs-12 col-sm-8 col-md-6">
<select name="{$fieldName}" class="form-control selectpicker">
{\\volist name="$vo.selectOptions" id="cvo"}
<option value="{\\$cvo.id}" {\\if $row['{$fieldName}']== $cvo.id}selected{\\/if}>{\\$cvo.title}</option>
{\\/volist}
</select>
</div>
</div>
{/case}
{case value='radio'}
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{$fieldComment}</label>
<div class="col-xs-12 col-sm-8 col-md-6">
{\\volist name="$vo.selectOptions" id="cvo"}
<label class="lyear-radio radio-primary radio-inline">
<input type="radio" name="{$fieldName}" {\\if $row.{$fieldName} == $key} checked{\\/if} value="{\\$key}">
<span>{\\$rvo}</span>
</label>
{\\/volist}
</div>
</div>
{/case}
{case value='time'}
<div class="form-group">
<label for="type" class="control-label col-xs-12 col-sm-2">{$fieldComment}</label>
<div class="col-xs-12 col-sm-8 col-md-6">
<input type="password" name="{$fieldName}" placeholder="请输入{$fieldComment}" value="{\$row.{$fieldName}|null}" class="form-control" />
</div>
</div>
{/case}
{case value='image'}
<div class="form-group">
<label for="type" class="control-label col-xs-12 col-sm-2">{$fieldComment}</label>
<div class="col-xs-12 col-sm-8 col-md-6">
<input id="c-{$fieldName}" class="form-control" size="50" name="{$fieldName}" type="hidden" value="{\$row.{$fieldName}|default=''}" data-tip="image">
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-{$fieldName}">
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.image" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-{$fieldName}" data-mimetype="image/*" data-multiple="false" data-preview-id="p-{$fieldName}"></a>
<a class="pic-add fachoose" style="height: auto;border: 0;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-{$fieldName}" data-mimetype="image/*" data-multiple="false" data-preview-id="p-{$fieldName}"></a>
</li>
</ul>
</div>
</div>
{/case}
{case value='file'}
<div class="form-group">
<label for="type" class="control-label col-xs-12 col-sm-2">{$fieldComment}</label>
<div class="col-xs-12 col-sm-8 col-md-6">
<input id="c-{$fieldName}" class="form-control" size="50" name="{$fieldName}" type="hidden" value="{\$row.{$fieldName}|default=''}" data-tip="image">
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-{$fieldName}">
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.image" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-{$fieldName}" data-multiple="false" data-preview-id="p-{$fieldName}"></a>
<a class="pic-add fachoose" style="height: auto;border: 0;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-{$fieldName}" data-multiple="false" data-preview-id="p-{$fieldName}"></a>
</li>
</ul>
</div>
</div>
{/case}
{case value='password'}
<div class="form-group">
<label for="type" class="control-label col-xs-12 col-sm-2">{$fieldComment}</label>
<div class="col-xs-12 col-sm-8 col-md-6">
<input type="password" name="{$fieldName}" placeholder="请输入{$fieldComment}" value="{\$row.{$fieldName}|null}" class="form-control" />
</div>
</div>
{/case}
{default /}
<div class="form-group">
<label for="type" class="control-label col-xs-12 col-sm-2">{$fieldComment}</label>
<div class="col-xs-12 col-sm-8 col-md-6">
<input type="text" name="{$fieldName}" placeholder="请输入{$fieldComment}" value="{\$row.{$fieldName}|null}" class="form-control" />
</div>
</div>
{/switch}
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8 col-md-6 layer-footer">
<button type="submit" class="btn btn-primary m-r-5">提交</button>
<button type="reset" class="btn btn-warning m-r-5">重置</button>
</div>
</div>
</form>
</div>
</div>
-43
View File
@@ -1,43 +0,0 @@
<?php
namespace app\middleware;
use support\Container;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use Webman\Route;
class ActionHook implements MiddlewareInterface
{
public function process(Request $request, callable $next) : Response
{
if ($request->controller) {
// 禁止直接访问beforeAction afterAction
if (substr($request->action,0,9) === '__before_' || substr($request->action,0,8) === '__after_') {
$callback = Route::getFallback() ?? function () {
return new Response(404, [], \file_get_contents(public_path() . '/404.html'));
};
$reponse = $callback($request);
return $reponse instanceof Response ? $reponse : \response($reponse);
}
$controller = Container::get($request->controller);
$beforeAction = '__before_'.$request->action.'__';
if (method_exists($controller, $beforeAction)) {
$before_response = call_user_func([$controller, $beforeAction], $request);
if ($before_response instanceof Response) {
return $before_response;
}
}
$response = $next($request);
$afterAction = '__after_'.$request->action.'__';
if (method_exists($controller, $afterAction)) {
$after_response = call_user_func([$controller, $afterAction], $request, $response);
if ($after_response instanceof Response) {
return $after_response;
}
}
return $response;
}
return $next($request);
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
/**
* Class StaticFile
* @package app\middleware
*/
class StaticFile implements MiddlewareInterface
{
public function process(Request $request, callable $next): Response
{
// Access to files beginning with. Is prohibited
if (strpos($request->path(), '/.') !== false) {
return response('<h1>403 forbidden</h1>', 403);
}
/** @var Response $response */
$response = $next($request);
// Add cross domain HTTP header
/*$response->withHeaders([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
]);*/
return $response;
}
}
-44
View File
@@ -1,44 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property string $title 名字
* @property string $network 网络
* @property string $address 账号
* @property string $img 图片
* @property integer $is_default 是否默认
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Address extends Base
{
//protected $name = 'address';
function getNetworkList(){
return [
"BEP-20"=>"BEP-20",
"TRC-20"=>"TRC-20",
"WECHAT"=>"微信",
"ALIPAY"=>"支付宝"
];
}
function getStatusList(){
return [
'0' => '禁用',
'1' => '启用',
];
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
-93
View File
@@ -1,93 +0,0 @@
<?php
namespace app\model;
use support\think\Db;
use traits\model\SoftDelete;
class Archives extends Base
{
//use SoftDelete;// 表名
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'append' => [
'status_text'
],
]);
}
public static function onAfterInsert(Archives $row)
{
\support\Log::error(''. json_encode($row));
$pk = $row->getPk();
self::where($pk, $row->$pk)->update(['weigh' => $row[$pk]]);
$changedData = $row->getData();
if (isset($changedData['content'])) {
//在更新成功后刷新副表
$values = array_intersect_key($changedData, array_flip(['content']));
$values['id'] = $row['id'];
//更新副表
Db::name('content')->insert($values, true);
}
}
public static function onAfterUpdate($row)
{
\support\Log::info('onAfterUpdate'.$row->id. json_encode($row->getChangedData()));
$changedData = $row->getChangedData();
if (isset($changedData['content'])) {
//在更新成功后刷新副表
$values = array_intersect_key($row->getData(), array_flip(['content']));
//更新副表
Db::name('content')->where('id',$row->id)->update($values);
}
}
public static function onAfterDelete($row)
{
Db::name('content')->where('id',$row->id)->delete();
}
/**
* 批量设置数据
* @param $data
* @return $this
*/
public function setAddonData(string|array|object $data)
{
if (is_object($data)) {
$data = get_object_vars($data);
}
foreach($data as $k=>$v){
$this->$k=$v;
}
return $this;
}
public function getStatusList()
{
return ['1' => '正常', '0' => '隐藏'];
}
public function getStatusTextAttr($value, $data)
{
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
$list = $this->getStatusList();
return isset($list[$value]) ? $list[$value] : '';
}
public function getCategoryOptions($type='default'){
return Category::where('status','1')->where('type',$type)->column('id,title');
}
function setCreatedAtAttr($v,$row=[]){
cp($v);
cp($row);
}
public function getFlagList()
{
return Config('site.flagtype');
}
public function category()
{
return $this->belongsTo('Category', 'category_id', 'id');//->setEagerlyType(0);
}
}
-233
View File
@@ -1,233 +0,0 @@
<?php
namespace app\model;
use Symfony\Component\Console\Input\Input;
use think\Model;
use think\facade\Db;
class BalanceLog extends Base
{
// 表结构定义(使用时间戳)
const TABLE_SCHEMA = [
'id' => 'int(11) NOT NULL AUTO_INCREMENT',
'user_id' => 'int(11) NOT NULL',
'currency' => 'varchar(20) NOT NULL',
'amount' => 'decimal(15,2) NOT NULL',
'before' => 'decimal(15,2) NOT NULL',
'after' => 'decimal(15,2) NOT NULL',
'type' => 'varchar(50) NOT NULL',
'created_at' => 'int(11) NOT NULL COMMENT \'UNIX timestamp\'', // 改为整型时间戳
'memo' => 'varchar(255) DEFAULT NULL',
'PRIMARY KEY (`id`)'
];
function getCreatedAtAttr($v){
return $v ? explode('.',$v)[0] : '';
}
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'connection' => 'mongodb',
// 'append' => [
// 'from_user',
// 'to_user'
// ],
]);
}
public static function create(array|object $data, array $allowField = [], bool $replace = false):\think\model\contract\Modelable
{
$model = new static();
if(isset($data['currency'])){
if(in_array($data['currency'],Config('site.allow_balance_log'))){
$data['status']=isset($data['status']) ? $data['status']:1;
$data['user_id'] = intval($data['user_id']);
$data['amount'] = floatval($data['amount']);
$data['before'] = floatval($data['before']);
$data['after'] = floatval($data['after']);
$data['type'] = $data['type'] instanceof \app\enum\BalanceType ? $data['type']->value : floatval($data['type']);
$model->setSuffix('_'.strtolower($data['currency']))->allowField($allowField)
->replace($replace)
->save($data, true);
}
}
return $model->fetchModel($model);
}
// 创建所有需要的表索引
public static function createAllIndexes(): array
{
$results = [];
$allow_balance_log = Config('site.allow_balance_log');
foreach ($allow_balance_log as $currency) {
$results[$currency] = self::createTableIndexes($currency);
}
return $results;
}
// 创建索引(适配时间戳查询)
public static function createTableIndexes(string $currency): array
{
$table = self::getTableName($currency);
$results = [];
try {
// 确保归档表存在
if (!self::tableExists($table)) {
self::createTableStructure($table);
}
// 主复合索引(使用时间戳)
if (!self::indexExists($table, 'idx_user_currency_type_created')) {
Db::execute("ALTER TABLE `{$table}` ADD INDEX `idx_user_currency_type_created` (`user_id`, `currency`, `type`, `created_at`)");
$results[] = "Created idx_user_currency_type_created on {$table}";
}
// 时间索引(降序优化)
if (!self::indexExists($table, 'idx_created_at')) {
Db::execute("ALTER TABLE `{$table}` ADD INDEX `idx_created_at` (`created_at` DESC)");
$results[] = "Created idx_created_at on {$table}";
}
} catch (\Throwable $e) {
$results['error'] = "Error on {$table}: " . $e->getMessage();
}
return $results;
}
// 检查索引是否存在
protected static function indexExists(string $table, string $indexName): bool
{
$indexes = Db::query("SHOW INDEX FROM `{$table}` WHERE Key_name = ?", [$indexName]);
return !empty($indexes);
}
// 检查表是否存在
protected static function tableExists(string $table): bool
{
try {
Db::query("SELECT 1 FROM `{$table}` LIMIT 1");
return true;
} catch (\Throwable $e) {
return false;
}
}
// 数据归档方法(可在定时任务中调用)
public static function archiveData(int $days = 3): array
{
$results = [];
$allow_balance_log = Config('site.allow_balance_log');
foreach ($allow_balance_log as $currency) {
$results[$currency] = self::archiveCurrencyData($currency, $days);
}
return $results;
}
// 归档指定货币的数据
protected static function archiveCurrencyData(string $currency, int $days): array
{
$table = self::getTableName($currency);
$archiveTable = $table . '_archive';
$cutoffTimestamp = time() - ($days * 86400); // 转为时间戳计算
$result = [
'table' => $table,
'archived' => 0,
'messages' => []
];
try {
// 确保归档表存在
if (!self::tableExists($archiveTable)) {
self::createTableStructure($archiveTable);
$result['messages'][] = "Created archive table: {$archiveTable}";
}
// 分批归档数据
$totalArchived = 0;
Db::table($table)
->where('created_at', '<=', $cutoffTimestamp)
->chunk(1000, function($logs) use ($archiveTable, $table, &$totalArchived) {
Db::table($archiveTable)->insertAll($logs);
$count = count($logs);
Db::table($table)->whereIn('id', array_column($logs, 'id'))->delete();
$totalArchived += $count;
});
$result['archived'] = $totalArchived;
$result['messages'][] = "Archived {$totalArchived} records from {$table}";
// 优化表
Db::execute("OPTIMIZE TABLE `{$table}`");
$result['messages'][] = "Optimized table: {$table}";
} catch (\Throwable $e) {
$result['error'] = $e->getMessage();
}
return $result;
}
// 查询方法(示例)
public static function queryLogs($userId, $currency, $type = null, $startTime = null, $endTime = null)
{
$model = new static;
$query = $model->setSuffix('_'.strtolower($currency))->where('currency', $currency)
->where('user_id', intval($userId))
->order('created_at', 'desc');
if ($type) {
if($type == '99999'){
$query->whereIn('type', [
\app\enum\BalanceType::OUTPUT_REWARD->value,
\app\enum\BalanceType::WITHDRAW_REWARD->value,
\app\enum\BalanceType::PRODUCT_INCOME->value,
\app\enum\BalanceType::AGENT_COMMISSION->value,
\app\enum\BalanceType::DIFFERENTIAL_COMMISSION->value
]);
}else{
$query->where('type', intval($type));
}
}
if ($startTime) {
// 支持传入时间戳或日期字符串
//$startTimestamp = is_numeric($startTime) ? intval($startTime) : strtotime($startTime);
$query->where('created_at', '>=', $startTime);
}
if ($endTime) {
// 支持传入时间戳或日期字符串
//$endTimestamp = is_numeric($endTime) ? intval($endTime) : strtotime($endTime);
$query->where('created_at', '<=', $endTime);
}
$limit = 10;
if(request()){
$limit = input('limit',10);
}
return $query->paginate($limit);
}
// 创建表结构
protected static function createTableStructure(string $table): bool
{
if (self::tableExists($table)) {
return false;
}
$columns = [];
foreach (self::TABLE_SCHEMA as $column => $definition) {
if (strpos($definition, 'PRIMARY KEY') === false) {
$columns[] = "`{$column}` {$definition}";
}
}
$primaryKey = self::TABLE_SCHEMA['PRIMARY KEY'] ?? 'PRIMARY KEY (`id`)';
$sql = "CREATE TABLE `{$table}` (" .
implode(', ', $columns) . ", " .
$primaryKey .
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
Db::execute($sql);
return true;
}
}
-51
View File
@@ -1,51 +0,0 @@
<?php
namespace app\model;
use DateTimeInterface;
use support\think\Model;
class Base extends Model
{
protected function getOptions(): array{
return [
'connection' => 'mysql',
'createTime' => 'created_at',
'updateTime' => 'updated_at',
'deleteTime' => 'deleted_at',
'autoWriteTimestamp' => 'int',
//'dateFormat' => false
// query 自定义数据库查询对象类名(默认为空)
// type 需要自动转换的字段及类型(数组,默认为空)
// autoValidate 是否自动验证(开启后会自动进行数据验证)
// validate 对应验证类名或验证规则(字符串或数组,autoValidate参数开启后有效)
// strict 是否严格区分字段大小写(默认为true)
// disuse 废弃字段(数组,默认为空)
// readonly 只读字段(数组,默认为空)
// hidden 输出隐藏字段(数组,默认为空)
// visible 输出显示字段(数组,默认为空)
// append 输出追加字段(数组,默认为空)
// mapping 字段映射(数组,默认为空)
// autoRelation 自动with关联(数组,默认为空)
// insert 自动新增写入(数组,默认为空)
// update 自动更新写入(数组,默认为空)
// dateFormat 时间输出格式化设置
];
}
/**
* 格式化日期
*
* @param DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
function getStatusList(){
return [
'0' => '隐藏',
'1' => '正常',
];
}
}
-21
View File
@@ -1,21 +0,0 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property float $amount 总价
* @property integer $type 类型
* @property string $title 标题
* @property integer $total 总数量
* @property integer $used 已使用的数量
* @property integer $expires 过期时间
* @property integer $days 量
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Card extends Base
{
protected $name = 'card';
}
-23
View File
@@ -1,23 +0,0 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(主键)
* @property string $username 用户名
* @property string $nickname 昵称
* @property string $password 密码
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property integer $status 禁用
*/
class Category extends Base
{
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 1
],
]);
}
}
-26
View File
@@ -1,26 +0,0 @@
<?php
namespace app\model;
/**
* Cdkey模型
*
* @package app\model
*
* @property integer $id 主键(ID) - 无注释
* @property integer $type 标题
* @property integer $category_id 分类ID
* @property string $account cdkey
* @property string $password 密码
* @property integer $days 量
* @property integer $expires 过期时间
* @property integer $is_used 是否使用
* @property integer $record_id 使用记录
* @property integer $use_time 使用时间
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Cdkey extends Base
{
protected $name = 'cdkey';
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $content 无注释
* @property string $content1 无注释
* @property string $content2 无注释
*/
class Content extends Base
{
}
-107
View File
@@ -1,107 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $title 标题
* @property string $image 封面
* @property string $amounts 面额列表
* @property integer $stock 库存
* @property integer $sales 銷售量
* @property integer $user_quantity 用户累计限购
* @property string $memo 备注
* @property integer $weight 权重
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
/**
--
-- 表的结构 `wa_gift`
--
CREATE TABLE `wa_gift` (
`id` int NOT NULL,
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',
`image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '封面',
`amounts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '面额列表',
`stock` int DEFAULT '0' COMMENT '库存',
`sales` int DEFAULT '0' COMMENT '銷售量',
`user_quantity` int NOT NULL DEFAULT '0' COMMENT '用户累计限购',
`memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`weight` int DEFAULT NULL COMMENT '权重',
`created_at` int DEFAULT NULL COMMENT '创建时间',
`updated_at` int DEFAULT NULL COMMENT '更新时间',
`status` tinyint DEFAULT NULL COMMENT '状态'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
--
-- 转存表中的数据 `wa_gift`
--
INSERT INTO `wa_gift` (`id`, `title`, `image`, `amounts`, `stock`, `sales`, `user_quantity`, `memo`, `weight`, `created_at`, `updated_at`, `status`) VALUES
(12, '京东礼品卡', '/upload/files/20250922/911fabdb0719edcbba3f0f7b84f59f85_68d09e2a8447e.png', '[\"1000\"]', 999, 405, 10, '', NULL, 1749809778, 1758502443, 1),
(13, '亚马逊电子礼品卡', '', '[1350.0]', 0, 530, 0, '', NULL, 1749809924, 1757263525, 1),
(14, '永辉电子礼品卡', '', '[3.5]', 0, 300, 0, '', NULL, 1749875587, 1757263532, 1),
(15, '盒马电子礼品卡', '', '[3.5]', 0, 300, 0, '', NULL, 1749953784, 1757263539, 1),
(42, '沃尔玛电子礼品卡', '', '[\"10\",\"22\"]', 99, 2, 0, '', NULL, 1757263545, 1759943587, 1);
--
-- 转储表的索引
--
--
-- 表的索引 `wa_gift`
--
ALTER TABLE `wa_gift`
ADD PRIMARY KEY (`id`) USING BTREE;
--
-- 在导出的表使用AUTO_INCREMENT
--
--
-- 使用表AUTO_INCREMENT `wa_gift`
--
ALTER TABLE `wa_gift`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=44;
COMMIT;
*/
class Gift extends Base
{
protected $autoWriteTimestamp = true;
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 1,
'amounts' => '[]'
]
]);
}
function setAmountsAttr($v='',$row=[]){
if(is_array($v) || is_object($v)){
return json_encode($v,JSON_UNESCAPED_UNICODE);
}
if(is_string($v) && substr($v,0,1)!='['){
$v = explode(',',$v);
return json_encode($v,JSON_UNESCAPED_UNICODE);
}
return '[]';
}
function getAmountsAttr($v='',$row=[]){
if(!$v){return [];}
if(is_array($v) || is_object($v)){
return $v;
}
return json_decode($v,true);
}
function getStatusList(){
return [
'0' => '禁用',
'1' => '启用',
];
}
}
-36
View File
@@ -1,36 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property integer $gift_id 产品ID
* @property integer $quantity 购买数量
* @property float $amount 总价
* @property integer $denomination 面值
* @property string $cdkey 兑换码
* @property string $memo CDKEY
* @property integer $status 状态
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class GiftOrder extends Base
{
public function gift()
{
return $this->belongsTo('Gift', 'gift_id', 'id');//->setEagerlyType(0);
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
function getStatusList(){
return [
'0' => '兑换中',
'1' => '成功',
'-1' => '失败',
];
}
}
-29
View File
@@ -1,29 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $code 无注释
* @property integer $expire 无注释
* @property integer $created_at 无注释
* @property integer $updated_at 无注释
* @property integer $status 无注释
*/
class Invitecode extends Base
{
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 0
]
]);
}
function getStatusList(){
return [
'0' => '隐藏',
'1' => '正常',
];
}
}
-72
View File
@@ -1,72 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* 产品模型
*
* @package app\model
* @property integer $id 主键(ID) - 无注释
* @property string $title 标题
* @property string $image 封面
* @property float $price 价格
* @property float $accelerate_price 加速包价格
* @property integer $min_score 最少获得积分
* @property integer $max_score 最多获得积分
* @property integer $sales 銷售量
* @property integer $total 问卷数量
* @property integer $assign_count 每日分配
* @property integer $accelerate_assign_times 加速包分配次数
* @property integer $accelerate_assign_count 加速包每日分配
* @property integer $user_quantity 用户累计限购
* @property string $memo 备注
* @property integer $weight 权重
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Product extends Base
{
function getCycleTypeList(){
return [
'hour' => '小时',
'day' => '天',
];
}
public function getCategoryOptions($type='product'){
return Category::where('status','1')->where('type',$type)->column('id,title');
}
public function category()
{
return $this->belongsTo('Category', 'category_id', 'id');//->setEagerlyType(0);
}
public function questionnaire()
{
return $this->belongsTo('Questionnaire', 'questionnaire_id', 'id');//->setEagerlyType(0);
}
function setStartTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getStartTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
function setEndTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getEndTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
}
-41
View File
@@ -1,41 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(主键)
* @property integer $user_id 用户ID
* @property integer $product_id 产品ID
* @property float $price 购买单价
* @property integer $quantity 购买数量
* @property float $amount 总价
* @property integer $accelerate 是否加速
* @property integer $accelerate_times 总加速次数
* @property integer $accelerate_used 已加速次数
* @property integer $assigned 总数量
* @property integer $total 已分配数量
* @property integer $assigned 已分配数量
* @property integer $assigned 已分配数量
* @property integer $status 禁用
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property \app\model\Product $product 产品模型
* @property \app\model\User $user 用户模型
*/
class ProductOrder extends Base
{
public function product()
{
return $this->belongsTo('Product', 'product_id', 'id');//->setEagerlyType(0);
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
// function getStatusList(){
// return \app\enum\WithdrawlStatus::toArray();
// }
}
-67
View File
@@ -1,67 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $category_id 分类ID
* @property string $country 国别
* @property integer $score 积分
* @property integer $start_time 开始时间
* @property integer $end_time 结束时间
* @property string $title 标题
* @property integer $total 题目数量
* @property string $body 题目详情
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Questionnaire extends Base
{
function setBodyAttr($v='',$row=[]){
if(is_array($v) || is_object($v)){
return json_encode($v,JSON_UNESCAPED_UNICODE);
}
return '[]';
}
function setStartTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getStartTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
function setEndTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getEndTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
public function getCategoryOptions(){
return Category::where('status','1')->where('type','questionnaire')->column('id,title');
}
function getBodyAttr($v='',$row=[]){
if($v){
return json_decode($v,true);
}
return [];
}
public function category()
{
return $this->belongsTo('Category', 'category_id', 'id');//->setEagerlyType(0);
}
}
-19
View File
@@ -1,19 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* 实名认证模型
* @property int $user_id
* @property string $realname
* @property string $idcard
* @property int $created_at
* @property int $updated_at
*/
class Realname extends Base
{
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
-92
View File
@@ -1,92 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $user_id 用户ID
* @property float $amount 金额
* @property string $network 网络
* @property string $address 充值地址
* @property string $extra 其他参数
* @property string $from 支付地址
* @property float $real_amount 实收金额
* @property string $txid 凭证
* @property integer $pay_time 支付时间
* @property integer $confirmations 确认数量
* @property string $result 结果
* @property string $reason 原因
* @property integer $status -1取消,0:创建,1支付中,2完成
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class Recharge extends Base
{
protected function getOptions(): array
{
// 所有的参数配置统一返回
return array_merge(parent::getOptions(),[
'append' => ['status_text','remaining_sec','usdt_amount']
]);
}
function getStatusTextAttr($v,$row){
if($v){
return \app\enum\RechargeStatus::tryFromValue($row['status'])->getDescription();
}
}
function getRemainingSecAttr($v,$row){
$created_at = $row['created_at'];
if(!$created_at){
return 0;
}
if(false !== strpos($created_at,'-')){
$created_at = strtotime($created_at);
}
$v = $created_at + 900 -time() ;
return $v <=0 ? 0 : $v;
}
function getUsdtAmountAttr($v,$row){
if(in_array($row['network'],['BEP-20','TRC-20'])){
$amount = bcdiv($row['amount'],Config('site.money_to_usdt_rate'),4);
//折扣
$amount = bcmul($amount,Config('site.usdt_recharge_discount'),4);
return formatAmount($amount,4);
}
return 0;
}
function setTransferAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
return $v;
}
function setCreatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
return $v;
}
function setUpdatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
return $v;
}
function getStatusList(){
return \app\enum\RechargeStatus::toArray();
}
function getNetworkList(){
return [
"BEP-20"=>"BEP-20",
"TRC-20"=>"TRC-20"
];
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
-210
View File
@@ -1,210 +0,0 @@
<?php
namespace app\model;
use support\think\Db;
/**
* 用户模型
* @package app\model\User
*
* @property integer $id 主键(ID) - 主键
* @property integer $role_id 角色ID
* @property integer $parent_id 推荐人
* @property integer $group 用戶分組
* @property string $username 用户名
* @property string $nickname 昵称
* @property string $password 密码
* @property string $trade_password 交易密码
* @property string $sex 性别
* @property string $avatar 头像
* @property string $email 邮箱
* @property string $mobile 手机
* @property integer $level 等级
* @property string $birthday 生日
* @property float $money 余额(元)
* @property float $score 积分
* @property float $currency1 无注释
* @property float $currency2 无注释
* @property float $currency3 无注释
* @property float $currency4 无注释
* @property float $currency5 无注释
* @property float $currency6 无注释
* @property float $currency7 无注释
* @property float $currency8 无注释
* @property float $currency9 无注释
* @property integer $email_verify 邮箱认证
* @property integer $mobile_verify 手机认证
* @property integer $realname_verify 实名认证
* @property string $safe_email 安全邮箱
* @property integer $loginfailure 登录失败的次数
* @property integer $last_time 登录时间
* @property string $last_ip 登录ip
* @property integer $join_time 注册时间
* @property string $join_ip 注册ip
* @property string $totp_secret totp_secret
* @property integer $expire_at 过期时间
* @property integer $active 激活状态
* @property string $invite_code 邀请码
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
* Strings methods
* @method static bool money(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 用户余额
* @method static bool score(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 用户积分
* @method static bool currency1(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 调研币
* @method static bool currency2(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency3(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency4(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency5(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency6(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 可领取指标
* @method static bool currency7(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 待分配指标
* @method static bool currency8(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 已分配指标
* @method static bool currency9(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 未通过指标
* @method static bool transform($from_currency,$to_currencyint $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*/
class User extends Base
{
public function role()
{
return $this->belongsTo('\\app\\model\\UserRole', 'role_id', 'id');//->bind(['name']);
}
public function realname()
{
return $this->hasOne('\\app\\model\\Realname', 'id', 'user_id');//->bind(['name']);
}
/**
* 扩展属性
* @param int $user_id 用户ID
* @return \think\model\relation\BelongsTo 用户扩展关联关系
*/
public function extend()
{
return $this->belongsTo('UserExtend', 'user_id', 'id');//->setEagerlyType(0);
}
// 定义与 UserTeam 的关联关系,假设用户的 id 对应 UserTeam 表中的 ancestor_id
public function team()
{
return $this->hasMany(UserTeam::class, 'ancestor_id', 'id');
}
public static function transform($from_currency,$to_currency,$user_id,$amount,\app\enum\BalanceType $type,$memo=null){
if(!in_array($from_currency,Config('site.allow_currencys'))){
abort(__('Incorrect from_currency:%currency%',['%currency%'=>$from_currency]));
}
if(!in_array($to_currency,Config('site.allow_currencys'))){
abort(__('Incorrect to_currency:%currency%',['%currency%'=>$to_currency]));
}
$time = time();
$user = self::lock(true)->where('id',$user_id)->find();
if(!$user){
throw new \Exception(__('User not found'));
}
$from_after = bcadd($user->{$from_currency} , -$amount,4);
$to_after = bcadd($user->{$to_currency} , $amount,4);
if($to_after<0 || $from_after<0){
abort(__('not enougth to currency'));
}
$from_logData = [
'user_id' => $user_id.'',
'currency' => $from_currency,
'amount' => -$amount.'',
'before' => $user->{$from_currency}.'',
'after' => $from_after.'',
'type' => $type->value,
'created_at' => $time.'',
'memo' => $memo
];
$to_logData = [
'user_id' => $user_id.'',
'currency' => $to_currency,
'amount' => $amount.'',
'before' => $user->{$to_currency}.'',
'after' => $to_after.'',
'type' => $type->value,
'created_at' => $time.'',
'memo' => $memo
];
$user->{$from_currency} = $from_after;
$user->{$to_currency} = $to_after;
$user->save();
// 写入日志
BalanceLog::create($to_logData);
BalanceLog::create($from_logData);
}
/**
* 变更会员余额
* @param int $score 积分
* @param int $user_id 会员ID
* @param string $memo 备注
*/
public static function _setBalance($currency,$user_id,$amount,\app\enum\BalanceType $type,$memo=null){
//cp($currency,$user_id,$amount,$type->getDescription(),$memo);
if(!in_array($currency,Config('site.allow_currencys'))){
abort(__('Incorrect currency:%currency%',['%currency%'=>$currency]));
}
if(!$currency){
abort(__('Incorrect currency'));
}
if(!$user_id){
abort(__('Incorrect parameter user'));
}
if(!$amount){
abort(__('Incorrect amount'));
}
$user = self::lock(true)->where('id',$user_id)->find();
$after = bcadd($user->{$currency}, $amount, 8);
if($amount < 0 && $after < 0){
abort(__('Insufficient user balance'));
}
$logData = [
'user_id' => $user_id.'',
'currency' => $currency,
'amount' => $amount.'',
'before' => '0',
'after' => '0',
'type' => $type->value,
'created_at' => time().'',
'memo' => $memo
];
$logData['before'] = $user->{$currency};
$user->{$currency} = $after;
$logData['after'] = $user->{$currency};
$user->save();
// 写入日志
BalanceLog::create($logData);
}
public static function __callStatic($method, $args)
{
$currency = strtolower($method);
if(in_array($currency,Config('site.allow_currencys'))){
return self::_setBalance($currency,$args[0],$args[1],$args[2],$args[3]);
}else{
return parent::__callStatic($method, $args);
}
}
/**
* 扩展属性
* @param int $user_id 用户ID
* @return \think\model\relation\BelongsTo 用户扩展关联关系
*/
public function referrer()
{
return $this->belongsTo('User', 'parent_id', 'id');//->setEagerlyType(0);
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $user_id 用户ID
* @property integer $direct_total 直推数量
* @property integer $team_total 团队成员数量
* @property float $consume 消费统计
* @property float $sales 销售额
*/
class UserExtend extends Base
{
}
-33
View File
@@ -1,33 +0,0 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(主键)
* @property string $name 角色名
* @property string $rules 权限
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property integer $pid 上级id
*/
class UserRole extends Base
{
public function setRulesAttr($v='',$row=[])
{
if(is_array($v)){
return implode(',',$v);
}
return $v;
}
/**
* @return mixed
*/
public function getRuleIds()
{
return $this->rules ? explode(',', $this->rules) : [];
}
}
-32
View File
@@ -1,32 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
use support\Model;
/**
* @property integer $id 主键(主键)
* @property string $title 标题
* @property string $icon 图标
* @property string $key 标识
* @property integer $pid 上级菜单
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property string $href url
* @property integer $type 类型
* @property integer $weight 排序
*/
class UserRule extends Base
{
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 1
],
]);
}
}
-15
View File
@@ -1,15 +0,0 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(ID) - 主键
* @property integer $user_id 用户ID
* @property string $sign_date 签到日期
* @property integer $reward 签到奖励
* @property integer $continuous_days 连续签到天数
* @property integer $created_at 创建时间
*/
class UserSignin extends Base
{
protected $name = 'user_signin';
protected $autoWriteTimestamp = true;
}
-56
View File
@@ -1,56 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $ancestor_id 上级用户ID
* @property integer $descendant_id 下级用户ID
* @property integer $depth 层级深度(0表示自己)
* @property integer $status 用户有效性状态,0表示无效,1表示有效
*/
class UserTeam extends Base
{
public function user()
{
return $this->belongsTo(User::class, 'descendant_id', 'id');
}
/**
* 根据用户ID向上查询团队成员
* @param mixed $user_id
* @param mixed $user_field
* @return array
*/
static function getTeamByChild($user_id = 0,$user_field=''){
$list = self::alias('ut')
->join('user u', 'ut.ancestor_id = u.id')
->where('ut.descendant_id', $user_id)
//->where('ut.ancestor_id','<>', $data['user_id'])
//->where('ut.depth', '<=', 3) // 限制三级内
->field('u.id as user_id,u.group, ut.depth')
->order('ut.depth ASC')->select();
if(!is_array($list)){
$list = $list->toArray();
}
return $list;
}
/**
* 根据用户ID向下查询团队
* @param mixed $user_id
* @param mixed $user_field
* @return array
*/
static function getTeamByParent($user_id = 0,$user_field=''){
$list = self::alias('ut')
->join('user u', 'ut.ancestor_id = u.id')
//->where('ut.descendant_id', $user_id)
->where('ut.ancestor_id','<>', $user_id)
//->where('ut.depth', '<=', 3) // 限制三级内
->field('u.id as user_id,u.group, ut.depth')
->order('ut.depth ASC')->select();
if(!is_array($list)){
$list = $list->toArray();
}
return $list;
}
}
-53
View File
@@ -1,53 +0,0 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property string $files 文件列表
* @property string $type 类型
* @property string $memo 原因
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class UserXuanchuan extends Base{
protected $name = 'user_xuanzhuan';
protected $autoWriteTimestamp = true;
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 0
],
]);
}
public static function onAfterUpdate($row)
{
$changedData = $row->getChangedData();
if (isset($changedData['status']) && $changedData['status']==1) {
if($row->type == 'pyq'){
\app\model\User::currency1($row->user_id,70,\app\enum\BalanceType::POSTPYQ);
}else{
\app\model\User::currency1($row->user_id,70,\app\enum\BalanceType::POSTGROUP);
}
}
}
function getStatusList(){
return [
'0' => '等待审核',
'1' => '审核通过',
'-1' => '审核失败',
];
}
function getTypeList(){
return [
'pyq' => '朋友圈',
'group' => 'QQ群'
];
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
-70
View File
@@ -1,70 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property float $deduction_amount 提现金额
* @property float $recive_amount 到账金额
* @property float $fee 后续费
* @property string $title 姓名
* @property string $network 方式
* @property string $address 地址
* @property integer $transfer_at 转账时间
* @property string $txid 凭证
* @property string $memo 备注
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Withdrawl extends Base
{
protected function getOptions(): array
{
// 所有的参数配置统一返回
return array_merge(parent::getOptions(),[
'append' => ['status_text']
]);
}
function getStatusTextAttr($v,$row){
if($v){
return \app\enum\WithdrawlStatus::tryFromValue($row['status'])->getDescription();
}
}
function setTransferAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
}
function setCreatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
}
function setUpdatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
}
function getNetworkList(){
return [
"BEP-20"=>"BEP-20",
"TRC-20"=>"TRC-20",
"WECHAT"=>"微信",
"ALIPAY"=>"支付宝"
];
}
function getStatusList(){
return \app\enum\WithdrawlStatus::toArray();
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
-213
View File
@@ -1,213 +0,0 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property integer $product_id 产品ID
* @property integer $questionnaire_id 问卷ID
* @property integer $order_id 订单ID
* @property array $answer 答案
* @property float $income 总收益
* @property integer $start_time 开始时间
* @property integer $end_time 结束时间
* @property string $settings 配置
* @property integer $status 状态
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class WorkRecord extends Base
{
protected function getOptions(): array
{
// 所有的参数配置统一返回
return array_merge(parent::getOptions(),[
'hidden' => ['settings'],
'append' => ['status_text']
]);
}
function getStatusTextAttr($v,$row){
return \app\enum\ServerStatus::tryFromValue($row['status']?:0)->getDescription();
}
public static function onBeforeWrite(&$row)
{
if(!$row->settings){
$step_before_array = [
'bootstrap' => rand(10,20),
'http_proxy' => rand(10,20),
'browser_env' => rand(10,20),
'enter_questionnaire' => rand(10,20),
'match_virtual_person' => rand(10,20),
];
//后置耗时
$step_after_array = [
'submit_result' => rand(5,10),
'wait_submit_result' => rand(5,10),
'wait_settlement' => 0,
];
$answer_array=[];
/** @var Questionnaire $questionnaire */
$questionnaire = Questionnaire::where('id',$row->questionnaire_id)->find();
for ($i=0; $i < $questionnaire->total ; $i++) {
$answer_array['answer_'.($i+1)] = 30;
}
$row->settings = array_merge($step_before_array,$answer_array,$step_after_array);
}
if(!$row->answer){
$answer = [];
foreach($row->questionnaire->body as $k=>$vo){
$answer[$k]= rand(0,count($vo['answer'])-1);
}
$row->answer = $answer;
}
// if($row->start_time && !$row->end_time){
// $row->end_time = 0;
// if(is_array($row->settings)){
// $end_time = $row->start_time + array_sum($row->settings);
// $row->end_time = $end_time;
// }
// }
//cp('onBeforeWrite');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
public function product()
{
return $this->belongsTo(Product::class, 'product_id', 'id');
}
public function questionnaire()
{
return $this->hasOne(Questionnaire::class, 'id', 'questionnaire_id');
}
/**
* 自动写入Settings字段
*/
function setSettingsAttr($v='',$row=[]){
if(is_array($v)){
return json_encode($v);
}
return $v;
}
/**
* Settings自动转为数组
*/
public function getSettingsAttr($v='',$row=[]){
//cp('getSettingsAttr');
if(!$v){
return [];
}
if(!is_array($v)){
return json_decode($v,true);
}
return $v;
}
public function start(){
if($this->status != \app\enum\ServerStatus::WAITING->value){
return $this->error(__('Server could not start'));
}
$this->status = \app\enum\ServerStatus::WORKING->value;
$this->start_time = time();
if(is_array($this->settings)){
$end_time = $this->start_time + array_sum($this->settings);
$this->end_time = $end_time;
}
$this->save();
addJob(['server_id'=>$this->id,'action'=>'workcomplete'],'Questionnaire',$this->end_time-time());
}
public function getStep(){
if ($this->status != \app\enum\ServerStatus::WORKING->value) {
return [
'step' => 0,
'msg' => \app\enum\ServerStatus::tryFromValue($this->status)->name,
'next_time' => null,
'remaining_seconds' => 0
];
}
$settings = $this->settings;
//cp($settings);
$start_time = $this->start_time; // 使用实际的开始时间
//$start_time = time();
$current_time = time();
//$current_time = $start_time+140;//time(); // 当前时间戳
if ($current_time < $start_time) {
//根据时间判断未开始
return [
'step' => 0,
'msg' => \app\enum\ServerStatus::tryFromValue($this->status)->name,
'next_time' => $start_time,
'remaining_seconds' => 0
];
}
// 计算已经过去的时间
$elapsed = $current_time - $start_time;
// 初始化结果
$result = [
'step' => 7,
'current' => __('bootstrap'),
'next_time' => null,
'msg' => __('anser'),
'remaining_seconds' => 0
];
// 遍历所有阶段
$accumulated = 0;
$found_current = false;
$stepArr = [
'bootstrap' => 1,
'http_proxy' => 2,
'browser_env' => 3,
'enter_questionnaire' => 4,
'match_virtual_person' => 5,
'submit_result' => 7,
'wait_submit_result' => 7,
'wait_settlement' => 7,
];
foreach ($settings as $name => $duration) {
$accumulated += $duration;
// 1. 找到当前阶段
if (!$found_current && $elapsed < $accumulated) {
$found_current = true;
if(substr($name,0,7) == 'answer_'){
$_name = explode('_',$name);
$result['index'] = intval($_name[1]);
$result['current'] = __($_name[0].'_%index%',['%index%'=>$result['index']]);
$result['step'] = 6;
$result['msg'] = 'AI Thinking';
}else{
$result['step'] = $stepArr[$name];
$result['msg'] = __($name);
}
// 当前阶段剩余时间 = 当前阶段结束时间 - 当前时间
$result['remaining_seconds'] = $accumulated - $elapsed;
}
// 2. 找到下一阶段开始时间
if ($elapsed < $accumulated) {
$result['next_time'] = $start_time + $accumulated;
break;
}
}
// 如果已经超过所有阶段
if ($elapsed >= $accumulated) {
return [
'step' => 7,
'msg' => \app\enum\ServerStatus::tryFromValue(4)->name,
'next_time' => null,
'remaining_seconds' => 0
];
}
return $result;
}
}
-79
View File
@@ -1,79 +0,0 @@
<?php
namespace app\queue\redis;
use Webman\RedisQueue\Consumer;
class Email implements Consumer
{
// 要消费的队列名
public $queue = 'Email';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
\support\Log::channel('mail')->alert("开始发送邮件");
$config = Config('site');
if(isset($data['config'])){
$config = $data['config'];
unset($data['config']);
}
try {
$mail = new \Nette\Mail\Message;
$mailer = new \Nette\Mail\SmtpMailer(
host: $config['mail_smtp_host'],
username: $config['mail_smtp_user'],
password: $config['mail_smtp_pass'],
encryption: \Nette\Mail\SmtpMailer::EncryptionSSL,
);
$mail->setFrom($config['mail_from'])
->addTo($data['email'])
->setSubject($data['title']);
if($data['body']){
$mail->setHtmlBody($data['body']);
}else{
$mail->setHtmlBody($this->getTemplate($data));
}
$mailer->send($mail);
\support\Log::channel('mail')->alert($data['email']."邮件已经发送");
} catch (\Throwable $th) {
\support\Log::channel('mail')->alert('发送邮件出错1:'.$th->getMessage());
\support\Log::channel('mail')->alert(json_encode($data));
}
// 无需反序列化
//var_export($data); // 输出 ['to' => 'tom@gmail.com', 'content' => 'hello']
}
function getTemplate($data){
$config = Config('site');
$mail_from = $config['mail_from'];
$str = '<div style="background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(46, 48, 50) !important; padding: 40px 8px; font-family: Poppins, serif, EmojiFont; text-align: center;height: 100%;display: flex;align-items: center;justify-content: center;" data-ogsb="rgb(239, 242, 244)"><table style="border-collapse: collapse; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(41, 41, 41) !important; max-width: 420px; margin: 0px auto; border-radius: 8px;" width="100%" role="presentation" cellspacing="0" cellpadding="40" border="0" data-ogsb="rgb(255, 255, 255)"><tbody><tr><td style="border-collapse: collapse; color: rgb(177, 180, 202) !important;" align="center" data-ogsc="rgb(89, 92, 112)"><img style="width: 96px; height: 96px; margin-top: 20px; color: rgb(177, 180, 202) !important;" title="logo" src="'.domain().'/static/img/mail_logo.png" data-imagetype="External" data-ogsc=""><div style="font-size: 24px; font-weight: 500; margin-top: 32px; color: rgb(177, 180, 202) !important;" data-ogsc="">Thanks for your request!</div><div style="font-size:12px; margin-top:8px; color:#a1a5b7">Do not share this code with anyone, even if they claim to be a Codify employee.</div><div style="padding:8px 20px; margin:32px 0; border-radius:100px; color:#00b2ff; border:2px #00b2ff dotted; display:inline-block; font-size:24px">{$code}</div><div style="margin-bottom:24px; color:#a1a5b7; font-size:12px">Valid within 15 minutes and <br>can only be used once.</div><div style="height: 1px; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(46, 48, 50) !important; width: 100%; color: rgb(177, 180, 202) !important;" data-ogsc="" data-ogsb="rgb(239, 242, 245)"></div><div style="font-size:12px; text-align:center; margin-top:24px; padding-bottom:20px; color:#a1a5b7">Need help with anything? connect <a style="color:#00b2ff" href="mailto: '.$mail_from.'" title="mailto: '.$mail_from.'" data-linkindex="0" id="LPlnk280375">support</a></div></td></tr></tbody></table></div>';
foreach($data as $k=>$v){
$str = str_replace('{$'.$k.'}',$v,$str);
}
return $str ;
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
echo "consume failure\n";
echo $e->getMessage() . "\n";
// 无需反序列化
//var_export($package);
}
}
-41
View File
@@ -1,41 +0,0 @@
<?php
namespace app\queue\redis;
use Webman\RedisQueue\Consumer;
class Job implements Consumer
{
// 要消费的队列名
public $queue = 'Default';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
// 无需反序列化
var_export($data); // 输出 ['to' => 'tom@gmail.com', 'content' => 'hello']
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
echo "consume failure\n";
echo $e->getMessage() . "\n";
// 无需反序列化
var_export($package);
}
}
-76
View File
@@ -1,76 +0,0 @@
<?php
namespace app\queue\redis;
use Webman\RedisQueue\Consumer;
class Sms implements Consumer
{
// 要消费的队列名
public $queue = 'Sms';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
\support\Log::channel('mail')->alert("开始发送短信:".json_encode($data));
$body = '';
if($data['body']){
$body = $data['body'];
}else{
$body = $this->getTemplate($data);
}
$url = 'http://api.smsbao.com/sms?u=aisiaisi8899&p='.md5('Aaa123123').'&m='.$data['mobile'].'&c='.urlencode($body);
$statusStr = array(
"0" => "短信发送成功",
"-1" => "参数不全",
"-2" => "服务器空间不支持,请确认支持curl或者fsocket,联系您的空间商解决或者更换空间!",
"30" => "密码错误",
"40" => "账号不存在",
"41" => "余额不足",
"42" => "帐户已过期",
"43" => "IP地址限制",
"50" => "内容含有敏感词"
);
try {
$res = get($url);
log_alert($res.$statusStr[$res]);
\support\Log::channel('mail')->alert($data['email']."短信已经发送");
} catch (\Throwable $th) {
\support\Log::channel('mail')->alert('发送短信出错:'.$th->getMessage());
\support\Log::channel('mail')->alert(json_encode($data));
}
// 无需反序列化
//var_export($data); // 输出 ['to' => 'tom@gmail.com', 'content' => 'hello']
}
function getTemplate($data){
$str = '【问析科技】验证码:{$code},该验证码在30分钟内输入有效,若非您本人操作请尽快修改登录密码';
foreach($data as $k=>$v){
$str = str_replace('{$'.$k.'}',$v,$str);
}
return $str ;
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
echo "consume failure\n";
echo $e->getMessage() . "\n";
// 无需反序列化
//var_export($package);
}
}
-54
View File
@@ -1,54 +0,0 @@
<?php
namespace app\queue\redis;
use Webman\RedisQueue\Consumer;
/**
* 延迟写入日志sql
*/
class Sql implements Consumer
{
// 要消费的队列名
public $queue = 'Sql';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
if(isset($data['model'])){
switch($data['model']){
case "RecordModel":
unset($data['model']);
\app\model\Record::create($data);
break;
case "RecordModel12":
break;
default:
return 1;
}
}
// 无需反序列化
//var_export($data); // 输出 ['to' => 'tom@gmail.com', 'content' => 'hello']
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
if($package['attempts'] >= $package['max_attempts']){
\support\Log::error(json_encode($package['data']));
}
}
}
-121
View File
@@ -1,121 +0,0 @@
<?php
namespace app\queue\single;
use app\model\BalanceLog;
use Webman\RedisQueue\Consumer;
use app\model\WorkRecord;
use think\facade\Db;
class Power implements Consumer
{
// 要消费的队列名
public $queue = 'Power';
public $connection = 'default';
// 消费Notify
public function consume($data)
{
return false;
try {
if($data['action'] == 'power_expris'){
$this->log('开始失效算力:'.json_encode($data));
$log = (new \app\model\BalanceLog)->setSuffix('_currency1')
->where('id',$data['id'])
->where('status',1)
->find();
if(!$log){
return ;
}
Db::startTrans();
try {
$log->status = 2;
$log->save();
\app\model\User::transform(
'currency1',
'currency2',
$log->user_id,
$log->amount,
\app\enum\BalanceType::POWER_EXPRIS,
$log->_id?:$log->id
);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
$this->log($e->getMessage());
throw $e;
}
}elseif($data['action'] == 'realese'){
$this->log('开始释放算力:'.json_encode($data));
$user = \app\model\User::find($data['user_id']);
$list = (new BalanceLog)->setSuffix('_currency1')
->where('user_id',intval($data['user_id']))
->where('status','<>',2)
->where('amount','>',0)
->order('created_at','asc')
->select();
$this->log($list->toArray());
//需要结算的记录
$_calcs = [];
//用户的待结算余额
$_currency1 = $user->currency1;
//用户的算力余额
$_score = $user->score;
foreach ($list as $key => $vo) {
//如果用户的待结算余额和算力都大于本记录的金额,就结算本条否则就结束计算
if($_currency1 - $vo->amount >= 0 && $_score-$vo->amount>=0){
//更新剩余货币
$_currency1-=$vo->amount;
$_score-=$vo->amount;
$_calcs[]=$vo;
continue;
}else{
break;
}
}
Db::startTrans();
try {
$transfrom_amount = 0;
foreach($_calcs as $k=>$log){
$transfrom_amount += $log->amount;
$log->status = 2;
$log->save();
}
if($transfrom_amount != 0){
\app\model\User::transform('score','money',$user->id,$transfrom_amount,\app\enum\BalanceType::POWER_REALESE);
\app\model\User::currency1($user->id,-$transfrom_amount,\app\enum\BalanceType::POWER_REALESE);
cache_add('user_income_total_'.$user->user_id,$transfrom_amount);
}
Db::commit();
} catch (\Exception $e) {
$this->log($e->getMessage());
Db::rollback();
}
}
}
catch(\Exception $e)
{
$this->log($e->getMessage());
}
}
function log($msg = ''){
log_alert($msg,'power');
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
$this->log('consume failure:'.$e->getMessage());
}
}
-246
View File
@@ -1,246 +0,0 @@
<?php
namespace app\queue\single;
use Webman\RedisQueue\Consumer;
use app\model\WorkRecord;
use app\model\User as UserModel;
use think\facade\Db;
use app\model\BalanceLog;
class Questionnaire implements Consumer
{
// 要消费的队列名
public $queue = 'Questionnaire';
public $connection = 'default';
// 消费Notify
public function consume($data)
{
try {
$this->log("云主机:".json_encode($data));
if($data['action'] == 'assign'){
$time = time();
if( !isset($data['user_id']) || !$data['user_id']){
$this->log('user_id==null' );
return ;
}
if( !isset($data['order_id']) || !$data['order_id']){
$this->log('order_id==null' );
return ;
}
/**
* @var \app\model\ProductOrder $order
*/
$order = \app\model\ProductOrder::with(['product'])
->where('id',$data['order_id'])
->where('user_id',$data['user_id'])
->whereColumn('assigned','<','total')
->lock(true)
->find();
if(!$order){
$this->log('订单不存在:'.$data['order_id'] );
return ;
}
/**
* @var \app\model\Product $product
*/
$product = $order->product;
// if($order->assigned >= $order->total){
// $this->log('订单已分配完:'.$order->id );
// return ;
// }
$amount = $product->assign_count;
if($order->accelerate && $order->accelerate_times > $order->accelerate_used){
$amount = $product->accelerate_assign_count;
}
$amount *= $order->quantity;
//每次分配不超过订单剩余量
$_amount = min($amount,($order->total - $order->assigned));
if($_amount <= 0 ){
return;
}
$user = UserModel::find($data['user_id']);
if($_amount > $user->currency7 ){
return;
}
//分配问卷
Db::startTrans();
try {
$currency6_logData = [
'user_id' => $data['user_id'].'',
'currency' => 'currency6',
'amount' => ''.$_amount,
'before' => $user->currency6.'',
'after' => ($user->currency6+$_amount).'',
'type' => \app\enum\BalanceType::ASSIGN_QUOTA->value,
'created_at' => $time.'',
'memo' => $order->id.''
];
$currency7_logData = [
'user_id' => $data['user_id'].'',
'currency' => 'currency7',
'amount' => '-'.$_amount,
'before' => $user->currency7.'',
'after' => ($user->currency7-$_amount).'',
'type' => \app\enum\BalanceType::ASSIGN_QUOTA->value,
'created_at' => $time.'',
'memo' => $order->id.''
];
$currency8_logData = [
'user_id' => $data['user_id'].'',
'currency' => 'currency8',
'amount' => ''.$_amount,
'before' => $user->currency8.'',
'after' => ($user->currency8+$_amount).'',
'type' => \app\enum\BalanceType::ASSIGN_QUOTA->value,
'created_at' => $time.'',
'memo' => $order->id.''
];
BalanceLog::create($currency6_logData);
BalanceLog::create($currency7_logData);
BalanceLog::create($currency8_logData);
$user->currency6+=$_amount; //可领取
$user->currency7-=$_amount; //待分配
$user->currency8+=$_amount; //已分配
$user->save();
$order->assigned += $_amount;
$order->accelerate_used +=1;
$order->save();
Db::commit();
if($order->total > $order->assigned){
//addJob($data,'Questionnaire',86400);
$nextday = strtotime('+1 days');
$nexttime = strtotime(datetime($nextday,'Y-m-d'))+$order->id-2000;
$nextdelay = $nexttime - time();
addJob($data,'Questionnaire',$nextdelay);
}
}catch(\Exception $e){
Db::rollback();
$this->log($e->getMessage());
throw $e;
}
}elseif($data['action'] == 'workcomplete'){
if( !isset($data['server_id']) || !$data['server_id']){
$this->log('server_id==null' );
return ;
}
$server = WorkRecord::find($data['server_id']);
if($server->status != \app\enum\ServerStatus::WORKING->value){
return ;
}
$auditing_time = rand(3600,10800); //1-3小时随机审核
$server->status = \app\enum\ServerStatus::AUDITING->value;
$server->save();
addJob(['server_id'=>$server->id,'action'=>'settlement'],'Questionnaire',$auditing_time);
}elseif($data['action'] == 'settlement'){
if( !isset($data['server_id']) || !$data['server_id']){
$this->log('server_id==null' );
return ;
}
$server = WorkRecord::find($data['server_id']);
if($server->status != \app\enum\ServerStatus::AUDITING->value){
return ;
}
Db::startTrans();
try {
//几率失败,最低5%-最高10%的几率失败
$success = true;
$failRate = rand(5, 10); // 随机选择失败率
$rand = rand(0, 99); // 0-99 共100个数
if ($rand < $failRate) {
$success = false;
}
$success = true;
if(!$success){
//失败的处理
$server->status = \app\enum\ServerStatus::FAILED->value;
$server->save();
$user = UserModel::find($server->user_id);
$time = time();
$currency6_logData = [
'user_id' => $server->user_id.'',
'currency' => 'currency6',
'amount' => '1',
'before' => $user->currency6.'',
'after' => ($user->currency6+1).'',
'type' => \app\enum\BalanceType::DIFFERENTIAL_COMMISSION,
'created_at' => $time.'',
'memo' => ''
];
$currency9_logData = [
'user_id' => $server->user_id.'',
'currency' => 'currency9',
'amount' => '1',
'before' => $user->currency9.'',
'after' => ($user->currency9+1).'',
'type' => \app\enum\BalanceType::DIFFERENTIAL_COMMISSION,
'created_at' => $time.'',
'memo' => ''
];
BalanceLog::create($currency6_logData);
BalanceLog::create($currency9_logData);
$user->currency6+=1; //可领取
$user->currency9+=1; //未通过
$user->save();
Db::commit();
}else{
//给用户付钱
UserModel::score($server->user_id,$server->income,\app\enum\BalanceType::PRODUCT_INCOME,$server->id);
$server->status = \app\enum\ServerStatus::COMPLETE->value;
$server->save();
cache_add('user_today_income_'.date('Ymd').'_'.$server->user_id,$server->income);
cache_add('user_month_income_'.date('Ym').'_'.$server->user_id,$server->income);
cache_add('user_income_total_'.$server->user_id,$server->income);
// $parent_info = parent_info($server->user_id);
// $parent_id = $parent_info['id'];
// // 产值奖励(直推)
// if(UserModel::where('id',$parent_id)->value('group') == 1){
// //只有渠道用户才能活得
// $reward = bcmul($data['amount'] ,0.05,4);
// UserModel::score($parent_id ,$reward,\app\enum\BalanceType::OUTPUT_REWARD,$data['id']);
// }
// //产值奖励
// $distributed_users = jicha($server->user_id,$server->income,[0,0.01,0.02,0.03,0.05,0.05]);
// foreach($distributed_users as $k=>$v){
// UserModel::money($v['user_id'],$v['amount'],\app\enum\BalanceType::OUTPUT_REWARD,$server->id);
// cache_add('user_income_total_'.$v['user_id'],$v['amount']);
// cache_add('user_output_reward_'.$v['user_id'],$v['amount']);
// }
Db::commit();
}
} catch (\Exception $e) {
Db::rollback();
$this->log($e->getMessage());
throw $e;
}
}else{
$this->log('未知状态');
}
}
catch(\Exception $e)
{
$this->log($e->getMessage());
}
}
function log($msg = ''){
\support\Log::channel('server')->alert($msg);
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
$this->log('consume failure:'.$e->getMessage());
}
}
-57
View File
@@ -1,57 +0,0 @@
<?php
namespace app\queue\single;
use Webman\RedisQueue\Consumer;
use app\model\WorkRecord;
use think\facade\Db;
class Studio implements Consumer
{
// 要消费的队列名
public $queue = 'Studio';
public $connection = 'default';
// 消费Notify
public function consume($data)
{
return false;
$this->log('开始结算:');
$list = (new \app\model\BalanceLog)->setSuffix('_currency3')
->where('status',1)
->order('created_at','asc')
->select();
if(!$list){
return ;
}
foreach($list as $log){
Db::startTrans();
try {
$log->status = 2;
$log->save();
\app\model\User::transform(
'currency3',
'money',
$log->user_id,
$log->amount,
\app\enum\BalanceType::STUDIO_REALESE,
$log->id
);
\app\model\User::currency4($log->user_id,$log->amount,\app\enum\BalanceType::STUDIO_REALESE,$log->id);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
$this->log($e->getMessage());
throw $e;
}
}
}
function log($msg = ''){
\support\Log::channel('studio')->alert($msg);
}
// 消费失败回调
public function onConsumeFailure(\Throwable $e, $package)
{
$this->log('consume failure:'.$e->getMessage());
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
namespace app\queue\single;
use Webman\RedisQueue\Consumer;
/**
* 团队奖励
*/
class buildTeam implements Consumer
{
// 要消费的队列名
public $queue = 'buildTeam';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
// 无需反序列化
//var_export($data); // 输出 ['to' => 'tom@gmail.com', 'content' => 'hello']
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
if($package['attempts'] >= $package['max_attempts']){
\support\Log::error(json_encode($package['data']));
}
}
}

Some files were not shown because too many files have changed in this diff Show More