初始化项目:添加后端代码、ThinkPHP框架、前端资源

This commit is contained in:
amb
2026-09-03 12:42:39 +08:00
commit 482bece22e
3726 changed files with 416708 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
<?php
namespace app\common\behavior;
use think\Config;
use think\Lang;
use think\Loader;
class Common
{
public function appInit()
{
$allowLangList = Config::get('allow_lang_list') ?? ['zh-cn', 'en'];
Lang::setAllowLangList($allowLangList);
}
public function appDispatch(&$dispatch)
{
$pathinfoArr = explode('/', request()->pathinfo());
if (!Config::get('url_domain_deploy') && $pathinfoArr && in_array($pathinfoArr[0], ['index', 'api'])) {
//如果是以index或api开始的URL则关闭路由检测
\think\App::route(false);
}
}
public function moduleInit(&$request)
{
// 设置mbstring字符编码
mb_internal_encoding("UTF-8");
// 如果修改了index.php入口地址,则需要手动修改cdnurl的值
$url = preg_replace("/\/(\w+)\.php$/i", '', $request->root());
// 如果未设置__CDN__则自动匹配得出
if (!Config::get('view_replace_str.__CDN__')) {
Config::set('view_replace_str.__CDN__', $url);
}
// 如果未设置__PUBLIC__则自动匹配得出
if (!Config::get('view_replace_str.__PUBLIC__')) {
Config::set('view_replace_str.__PUBLIC__', $url . '/');
}
// 如果未设置__ROOT__则自动匹配得出
if (!Config::get('view_replace_str.__ROOT__')) {
Config::set('view_replace_str.__ROOT__', preg_replace("/\/public\/$/", '', $url . '/'));
}
// 如果未设置cdnurl则自动匹配得出
if (!Config::get('site.cdnurl')) {
Config::set('site.cdnurl', $url);
}
// 如果未设置cdnurl则自动匹配得出
if (!Config::get('upload.cdnurl')) {
Config::set('upload.cdnurl', $url);
}
if (Config::get('app_debug')) {
// 如果是调试模式将version置为当前的时间戳可避免缓存
Config::set('site.version', time());
// 如果是开发模式那么将异常模板修改成官方的
Config::set('exception_tmpl', THINK_PATH . 'tpl' . DS . 'think_exception.tpl');
}
// 如果是trace模式且Ajax的情况下关闭trace
if (Config::get('app_trace') && $request->isAjax()) {
Config::set('app_trace', false);
}
// 切换多语言
if (Config::get('lang_switch_on')) {
$lang = $request->get('lang');
if (preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang)) {
\think\Cookie::set('think_var', $lang);
}
}
// Form别名
if (!class_exists('Form')) {
class_alias('fast\\Form', 'Form');
}
}
public function addonBegin(&$request)
{
// 加载插件语言包
$lang = request()->langset();
$lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn';
Lang::load([
APP_PATH . 'common' . DS . 'lang' . DS . $lang . DS . 'addon' . EXT,
]);
$this->moduleInit($request);
}
}
@@ -0,0 +1,192 @@
<?php
namespace app\common\behavior\app\agent;
use app\common\model\order\Item;
use app\common\model\app\agent\Goods;
use app\common\model\app\agent\Order as AgentOrder;
/**
* 分销等级
*/
class Commission
{
/**
* 分佣金 创建订单
* @param $order 订单
* @return bool
*/
public function run($orderNo)
{
$agentConfig = \app\common\model\app\Config::getConfig('agent');
if(!$agentConfig){
return false;
}
//未开启分销
if($agentConfig['status'] != 1){
return false;
}
$order = \app\common\model\order\Order::where([
'order_no'=>$orderNo
])->find();
if(!$order){
return false;
}
$this->addLog($order);
return true;
}
/**
* 添加记录
* @param $order
* @return false|void
*/
public function addLog($order){
$agentConfig = \app\common\model\app\Config::getConfig('agent');
$orderUserRelation = [
'parent'=>false,
'grandpa'=>false
];
if($order->source == 'channels'){
//获取视频号分享员信息
if(!\app\admin\library\project\App::isInstall('channels')){
return false;
}
$channelsOrder = (new \app\admin\model\app\channels\Order())->getOrderDetail($order->order_no);
if(!$channelsOrder || !$channelsOrder->member || !$channelsOrder->member->user_id || $channelsOrder->member->status != 1){
return false;
}
$orderUserRelation['parent']['parent_id'] = $channelsOrder->member->user_id;
if($orderUserRelation['parent']['parent_id']){
$orderUserRelation['grandpa'] = \app\common\model\app\agent\Relation::getParentUser($channelsOrder->member->user_id);
}
}else{
//找该下单用户的上级关系
$orderUserRelation = \app\common\model\app\agent\Relation::getUserRelation($order->user_id);
if(!$orderUserRelation['parent'] && !$orderUserRelation['grandpa']){
return false;
}
}
//计算佣金
$orderItemList = (new Item())->getItemList($order->order_no);
if(!empty($orderItemList)){
foreach ($orderItemList as $orderItem){
//找该下单用户的上级 商品佣金
try{
if($orderUserRelation['parent']){
$parentProp = Goods::getUserGoodsProp($orderUserRelation['parent']['parent_id'],$orderItem['item_id'],$orderItem['goods_type'],$orderItem['total_price']);
if($parentProp){
$data = [
'uniacid'=>UNIACID,
'order_user_id'=>$order->user_id,
'beneficiary_user_id'=>$orderUserRelation['parent']['parent_id'],
'order_no'=>$order->order_no,
'goods_id'=>$orderItem['item_id'],
'goods_type'=>$orderItem['goods_type'],
'price'=>$orderItem['real_price'],
'order_item_id'=>$orderItem['id'],
'brokerage_price'=>$parentProp['price']['goods'],//
'brokerage_prop'=>$parentProp['prop']['goods'],
'type'=>'commission',
'mode'=>$parentProp['mode'],
'status'=>0,
'createtime'=>time()
];
AgentOrder::insert($data);
}
}
if($agentConfig['mode'] == 2 && $orderUserRelation['grandpa']){
//继续分佣 找该下单用户的上级的上级 下级分销员卖货提成
$grandpaProp = Goods::getUserGoodsProp($orderUserRelation['grandpa']['parent_id'],$orderItem['item_id'],$orderItem['goods_type'],$orderItem['real_price']);
if($grandpaProp){
$data = [
'uniacid'=>UNIACID,
'order_user_id'=>$order->user_id,
'beneficiary_user_id'=>$orderUserRelation['grandpa']['parent_id'],
'order_no'=>$order->order_no,
'goods_id'=>$orderItem['item_id'],
'goods_type'=>$orderItem['goods_type'],
'order_item_id'=>$orderItem['id'],
'price'=>$orderItem['real_price'],
'brokerage_price'=>$grandpaProp['price']['customer'],//
'brokerage_prop'=>$grandpaProp['prop']['customer'],
'type'=>'subordinate',
'mode'=>$grandpaProp['mode'],
'status'=>0,
'createtime'=>time()
];
AgentOrder::insert($data);
}
}
}catch(\think\Exception $e){
continue;
}
}
}
}
/**
* 结算
* @return void
*/
public function settle($orderNo){
$logList = AgentOrder::where([
'order_no'=>$orderNo,
'status'=>0
])->select();
AgentOrder::where([
'order_no'=>$orderNo
])->update([
'status'=>1
]);
if($logList){
foreach ($logList as $log){
$memo = $log['type'] == 'commission' ? '商品佣金' : '下级分销员卖货提成';
\app\api\model\app\agent\Money::changeMoney($log['beneficiary_user_id'],'money',$log['brokerage_price'],false,$memo);
//检查升级
(new \app\api\model\app\agent\Level())->handle($log['beneficiary_user_id']);
}
}
return true;
}
/**
* 取消订单后 作废分佣记录
* @param $orderNo
* @return bool
*/
public function cancelOrder($orderNo){
AgentOrder::where([
'order_no'=>$orderNo
])->update([
'status'=>2
]);
return true;
}
}
@@ -0,0 +1,22 @@
<?php
namespace app\common\behavior\live;
/**
* 抖音订单同步
*/
class Goods
{
// 订单支付成功,担保支付订单同步
public function run($data)
{
$actionModel = new \app\admin\model\live\GoodsAction;
$actionModel->where([
'id'=>$data['live_goods_action_id'],
'user_id'=>$data['user_id']
])->update([
'order_no'=>$data['order_no']
]);
}
}
@@ -0,0 +1,54 @@
<?php
namespace app\common\behavior\order;
/**
* 抖音订单同步
*/
class Douyin
{
// 订单支付成功,担保支付订单同步
public function run($orderNo)
{
$data = (new \app\common\model\order\Order)->getOrderDetail($orderNo,['payment_json']);
if(!$data || $data['status'] != \app\common\constant\order\Status::STATUS_SUCCESS || $data['real_price'] == 0){
return false;
}
$data['payment_json'] = json_decode($data['payment_json'],true);
if(!$data['payment_json'] || !isset($data['payment_json']['open_id'])){
return false;
}
$orderItem = [];
$amount = 0;
foreach ($data['goodsList'] as $item){
$orderItem[] = [
'item_code'=>(string)$item['item_id'],//开发者侧商品 ID
'img'=>$item['snapshoot']['cover'],//子订单商品图片 URL
'title'=>$item['snapshoot']['name'],//子订单商品介绍标题
'amount'=>(int)$item['count'],//单类商品的数目
'price'=>(int)($item['snapshoot']['price'] * 100)//单类商品的总价,单位为分
];
$amount += $item['count'];
}
$orderDetail = [
'order_id'=>$data['order_no'],//开发者侧业务单号
'create_time'=>(int)($data['createtime'] . '123'),//订单创建的时间,13 位毫秒时间戳
'status'=>'已支付',//订单状态,建议采用以下枚举值
'amount'=>(int)$amount,//订单商品总数
'total_price'=>(int)($data['real_price'] * 100),//订单总价,单位为分
'detail_url'=>'pages/order/detail/detail?order_no='.$data['order_no'],//小程序订单详情页 path例如pages/order/orderDetail
'item_list'=>$orderItem
];
$paymentConfig = \app\common\model\config\System::getConfig('douyinpay');
$ret = \douyin\Byte::init($paymentConfig)->pushOrder($data['payment_json']['open_id'],$orderDetail);
}
}
+262
View File
@@ -0,0 +1,262 @@
<?php
namespace app\common\behavior\order;
use app\common\model\user\Subscription;
use app\common\model\order\Item;
use app\admin\model\course\Course;
/**
* 支付成功
*/
class Payed
{
// 订单支付成功
public function run($order) {
$userId = $order->user_id;
//虚拟商品交易成功
if($order->order_type == 'physical'){
$order->status = \app\common\constant\order\Status::STATUS_UNSEND;
\app\common\model\order\Log::set($order->order_no,"订单已完成支付,等待发货");
$orderItemList = (new Item())->getItemList($order->order_no);
if(!empty($orderItemList)){
$stockSale = new \app\common\service\app\physical\StockSale();
$stockSale->forwardStockSale($orderItemList);
}
} else {
$order->status = \app\common\constant\order\Status::STATUS_SUCCESS;
//增加销量
\app\common\model\order\Log::set($order->order_no,"订单已完成交易");
// 订单支付成功
$this->courseSubscriptionBind($order);
}
$order->save();
//分销 检查升级
(new \app\api\model\app\agent\Level())->handle($userId);
//消息推送收集
\app\admin\library\app\msgpush\Msg::collect('order_success',['order_no'=>$order->order_no]);
}
/**
* 授权用户购买的课程
* @param $order
* @return bool
*/
public function courseSubscriptionBind($order){
$orderItemList = (new Item())->getItemList($order->order_no);
$SubscriptionModel = new Subscription();
if(!empty($orderItemList)){
foreach ($orderItemList as $orderItem){
switch ($order->order_type){
case 'vipcard':
$cardSku = explode("_",$orderItem['extend_data']);
if($cardSku){
\app\common\model\app\vip\CardUser::openVip($order->user_id,$orderItem['item_id'],$cardSku[1]);
}
break;
case 'live_gift':
$messageInfo = explode("_",$orderItem['extend_data']);
\app\common\model\live\Message::sendMessage($order->user_id,$messageInfo[0],$messageInfo[1],'gift',$orderItem['item_name']."×".$orderItem['count'],1);
//增加送礼物记录
\app\admin\model\live\GiftLog::insert([
'uniacid'=>UNIACID,
'live_id'=>$messageInfo[0],
'user_id'=>$order->user_id,
'order_no'=>$order->order_no,
'name'=>$orderItem['item_name'],
'price'=>$order->real_price,
'count'=>$orderItem['count'],
'createtime'=>time()
]);
break;
case 'exercises':
\app\common\model\app\exam\ExercisesSubscribe::subscribe($order->user_id,$orderItem['item_id'],'pay');
break;
case 'activity':
$ticketInfo = $orderItem->snapshoot;
\app\api\model\app\activity\Activity::setUserTicket($orderItem->user_id,$orderItem->item_id,$ticketInfo['ticket_id'],$ticketInfo['apply_info_id'],$orderItem->count,$orderItem->real_price,$orderItem->order_no,$orderItem->extend_data);
//增加销量
//活动增加销量
\app\api\model\app\activity\Activity::where(['id'=>$orderItem['item_id']])->setInc('sales');
//活动票也要增加
\app\api\model\app\activity\Ticket::where(['id'=>$ticketInfo['ticket_id']])->setInc('sales');
break;
case 'composite':
// 组合商品处理:授权子商品权限 + 增加销量
if (\app\admin\library\project\App::isInstall('composite')) {
$this->compositePurchaseHandle($orderItem, $order);
}
break;
default:
$this->addSales($orderItem['item_id'],$order);
$getType = '';
$mobile = '';
if($order->source == 'channels'){
$channelsOrder = \think\Db::name("app_channels_order")->where(['order_id'=>$order->order_no])->find();
if($channelsOrder){ $getType = 'channels'; $mobile = $channelsOrder['mobile']; }
}
$SubscriptionModel->setUserCourse($order->user_id,$orderItem['item_id'],$getType,$mobile);
break;
}
}
}
return true;
}
/**
* 增加销量
* @param $courseId
* @return void
*/
public function addSales($goodsId,$order){
switch ($order->order_type){
case 'score':
//积分商品增加销量 减少库存
$scoreGoodsModel = new \app\api\model\app\score\Goods;
$scoreGoodsModel->where(['goods_id'=>$goodsId])->setInc('sales');
$scoreGoodsModel->where(['goods_id'=>$goodsId])->setDec('stock');
break;
case 'goods':
Course::where(['id'=>$goodsId])->setInc('sales');
break;
}
return true;
}
/**
* 处理组合商品购买:授权子商品 + 增加销量
* @param array $orderItem 订单商品
* @param object $order 订单对象
*/
protected function compositePurchaseHandle($orderItem, $order)
{
$snapshoot = $orderItem['snapshoot'];
if (is_string($snapshoot)) {
$snapshoot = json_decode($snapshoot, true);
}
if (empty($snapshoot)) {
return;
}
$subGoodsList = $snapshoot['sub_goods_list'] ?? [];
$userId = $order->user_id;
$compositeGoodsId = $orderItem['item_id'];
// 授权每个子商品
$SubscriptionModel = new Subscription();
foreach ($subGoodsList as $subGoods) {
$subGoodsId = $subGoods['sub_goods_id'];
$subGoodsType = $subGoods['sub_goods_type'];
// 构建有效期覆盖配置(组合商品自定义有效期优先于商品本身有效期)
$validityOverride = null;
if (!empty($subGoods['validity_type']) && intval($subGoods['validity_type']) > 1) {
$validityOverride = [
'validity_type' => intval($subGoods['validity_type']),
'validity_value' => $subGoods['validity_value'] ?? '',
];
}
switch ($subGoodsType) {
case 'course':
// 视频/音频/图文/直播都统一为 course 授权
$SubscriptionModel->setUserCourse($userId, $subGoodsId, '', '', $validityOverride);
// 增加子商品销量
Course::where(['id' => $subGoodsId])->setInc('sales');
break;
case 'vipcard':
// 会员卡:从快照中取有效期信息
$cardSku = $subGoods['sub_goods_price'] . '_' . ($subGoods['validity_value'] ?? '30');
\app\common\model\app\vip\CardUser::openVip($userId, $subGoodsId, $cardSku);
break;
case 'exercises':
\app\common\model\app\exam\ExercisesSubscribe::subscribe($userId, $subGoodsId, 'pay');
// 增加练习销量
\think\Db::name('app_exam_exercises')->where(['id' => $subGoodsId])->setInc('sales');
break;
case 'audio':
case 'live':
case 'article':
case 'column':
// 音频/直播/图文/专栏统一存储于 tuzhi_course 表,按 id 累加销量
$SubscriptionModel->setUserCourse($userId, $subGoodsId, '', '', $validityOverride);
\think\Db::name('course')->where(['id' => $subGoodsId])->setInc('sales');
break;
}
}
// 增加组合商品自身销量
\app\admin\model\app\composite\Goods::where([
'id' => $compositeGoodsId,
'uniacid' => UNIACID
])->setInc('total_sales');
}
/**
* 处理组合商品退款:关闭子商品权限 + 返还销量
* @param array $orderItem 订单商品
* @param object $order 订单对象
* @param bool $isFullRefund 是否全额退款
*/
public static function compositeRefundHandle($orderItem, $order, $isFullRefund = false)
{
$snapshoot = $orderItem['snapshoot'];
if (is_string($snapshoot)) {
$snapshoot = json_decode($snapshoot, true);
}
if (empty($snapshoot)) {
return;
}
$subGoodsList = $snapshoot['sub_goods_list'] ?? [];
$userId = $order['user_id'] ?? $order->user_id;
$compositeGoodsId = $orderItem['item_id'];
// 如果全额退款,返还销量
if ($isFullRefund) {
// 组合商品销量 -1
\app\admin\model\app\composite\Goods::where([
'id' => $compositeGoodsId,
'uniacid' => UNIACID
])->setDec('total_sales');
// 子商品销量 -1
foreach ($subGoodsList as $subGoods) {
switch ($subGoods['sub_goods_type']) {
case 'course':
Course::where(['id' => $subGoods['sub_goods_id']])->setDec('sales');
break;
case 'exercises':
\think\Db::name('app_exam_exercises')->where(['id' => $subGoods['sub_goods_id']])->setDec('sales');
break;
case 'audio':
case 'live':
case 'article':
case 'column':
// 音频/直播/图文/专栏统一存储于 tuzhi_course 表,按 id 扣减销量
\think\Db::name('course')->where(['id' => $subGoods['sub_goods_id']])->setDec('sales');
break;
}
}
}
}
}