diff --git a/plugs/think-plugs-points-mall/composer.json b/plugs/think-plugs-points-mall/composer.json index 106be4d..d45acf7 100644 --- a/plugs/think-plugs-points-mall/composer.json +++ b/plugs/think-plugs-points-mall/composer.json @@ -14,6 +14,9 @@ "php": ">7.1" }, "autoload": { + "files": [ + "./src/helper.php" + ], "psr-4": { "plugin\\points_mall\\": "src" } diff --git a/plugs/think-plugs-points-mall/src/controller/Goods.php b/plugs/think-plugs-points-mall/src/controller/Goods.php index 0b90465..3c0c642 100644 --- a/plugs/think-plugs-points-mall/src/controller/Goods.php +++ b/plugs/think-plugs-points-mall/src/controller/Goods.php @@ -2,7 +2,15 @@ namespace plugin\points_mall\controller; +use HttpResponseException; +use plugin\points_mall\model\PointsMallGoodsStock; +use plugin\points_mall\model\PointsMallGoods; +use plugin\points_mall\model\PointsMallGoodsCate; +use plugin\points_mall\model\PointsMallGoodsItem; +use plugin\points_mall\service\GoodsService; use think\admin\Controller; +use think\admin\extend\CodeExtend; +use think\admin\helper\QueryHelper; /** * 商品管理 @@ -10,13 +18,211 @@ use think\admin\Controller; class Goods extends Controller { /** - * 商品管理 + * 商品数据管理 * @auth true * @menu true * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException */ public function index() { - $this->title = '商品管理'; + $this->type = $this->request->get('type', 'index'); + PointsMallGoods::mQuery($this->get)->layTable(function () { + $this->title = '商品数据管理'; + $this->cates = PointsMallGoodsCate::items(); + }, function (QueryHelper $query) { + $query->withoutField('specs,content')->like('code|name#name')->like('marks,cates', ','); + $query->equal('status,level_upgrade,delivery_code,rebate_type')->dateBetween('create_time'); + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + }); + } + + /** + * 商品选择器 + * @login true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function select() + { + $this->get['status'] = 1; + $this->get['deleted'] = 0; + $this->index(); + } + + /** + * 添加商品数据 + * @auth true + */ + public function add() + { + $this->mode = 'add'; + $this->title = '添加商品数据'; + PointsMallGoods::mForm('form', 'code'); + } + + /** + * 编辑商品数据 + * @auth true + */ + public function edit() + { + $this->mode = 'edit'; + $this->title = '编辑商品数据'; + PointsMallGoods::mForm('form', 'code'); + } + + /** + * 复制编辑商品 + * @auth true + */ + public function copy() + { + $this->mode = 'copy'; + $this->title = '复制编辑商品'; + PointsMallGoods::mForm('form', 'code'); + } + + /** + * 表单数据处理 + * @param array $data + */ + protected function _copy_form_filter(array &$data) + { + if ($this->request->isPost()) { + $data['code'] = CodeExtend::uniqidNumber(16, 'G'); + } + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'G'); + } + if ($this->request->isGet()) { + $this->cates = PointsMallGoodsCate::items(true); + $data['marks'] = $data['marks'] ?? []; + $data['cates'] = $data['cates'] ?? []; + $data['specs'] = json_encode($data['specs'] ?? [], 64 | 256); + $data['items'] = PointsMallGoodsItem::itemsJson($data['code']); + $data['slider'] = is_array($data['slider'] ?? []) ? join('|', $data['slider'] ?? []) : ''; + $data['delivery_code'] = $data['delivery_code'] ?? 'FREE'; + } elseif ($this->request->isPost()) try { + if (empty($data['cover'])) $this->error('商品图片不能为空!'); + if (empty($data['slider'])) $this->error('轮播图片不能为空!'); + // 商品规格保存 + [$count, $items] = [0, json_decode($data['items'], true)]; + $data['marks'] = arr2str($data['marks'] ?? []); + foreach ($items as $item) if ($item['status'] > 0) { + $count++; + $data['price_market'] = min($data['price_market'] ?? $item['market'], $item['market']); + $data['price_selling'] = min($data['price_selling'] ?? $item['selling'], $item['selling']); + $data['allow_balance'] = max($data['allow_balance'] ?? $item['allow_balance'], $item['allow_balance']); + $data['allow_integral'] = max($data['allow_integral'] ?? $item['allow_integral'], $item['allow_integral']); + } + if (empty($count)) $this->error('无效的的商品价格信息!'); + $this->app->db->transaction(static function () use ($data, $items) { + // 标识所有规格无效 + PointsMallGoodsItem::mk()->where(['gcode' => $data['code']])->update(['status' => 0]); + $model = PointsMallGoods::mk()->where(['code' => $data['code']])->findOrEmpty(); + $model->{$model->isExists() ? 'onAdminUpdate' : 'onAdminInsert'}($data['code']); + $model->save($data); + // 更新或写入商品规格 + foreach ($items as $item) PointsMallGoodsItem::mUpdate([ + 'gsku' => $item['gsku'], + 'ghash' => $item['hash'], + 'gcode' => $data['code'], + 'gspec' => $item['spec'], + 'gimage' => $item['image'], + 'status' => $item['status'] ? 1 : 0, + 'price_cost' => $item['cost'], + 'price_market' => $item['market'], + 'price_selling' => $item['selling'], + 'allow_balance' => $item['allow_balance'], + 'allow_integral' => $item['allow_integral'], + 'number_virtual' => $item['virtual'], + 'number_express' => $item['express'], + 'reward_balance' => $item['balance'], + 'reward_integral' => $item['integral'], + ], 'ghash', ['gcode' => $data['code']]); + }); + // 刷新产品库存 + GoodsService::stock($data['code']); + $this->success('商品编辑成功!', 'javascript:history.back()'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + throw $exception; +// $this->error($exception->getMessage()); + } + } + + /** + * 商品库存入库 + * @auth true + * @return void + */ + public function stock() + { + $input = $this->_vali(['code.require' => '商品不能为空哦!']); + if ($this->request->isGet()) { + $this->vo = PointsMallGoods::mk()->where($input)->with('items')->findOrEmpty()->toArray(); + empty($this->vo) ? $this->error('无效的商品!') : $this->fetch(); + } else try { + [$data, $post, $batch] = [[], $this->request->post(), CodeExtend::uniqidDate(12, 'B')]; + if (isset($post['gcode']) && is_array($post['gcode'])) { + foreach (array_keys($post['gcode']) as $key) if ($post['gstock'][$key] > 0) $data[] = [ + 'batch_no' => $batch, + 'ghash' => $post['ghash'][$key], + 'gcode' => $post['gcode'][$key], + 'gspec' => $post['gspec'][$key], + 'gstock' => $post['gstock'][$key], + ]; + empty($data) || PointsMallGoodsStock::mk()->saveAll($data); + } + GoodsService::stock($input['code']); + $this->success('库存更新成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $e) { + throw $e; +// $this->error($e->getMessage()); + } + } + + /** + * 商品上下架 + * @auth true + */ + public function state() + { + PointsMallGoods::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ]), 'code'); + } + + /** + * 删除商品数据 + * @auth true + */ + public function remove() + { + PointsMallGoods::mSave($this->_vali([ + 'code.require' => '编号不能为空!', + 'deleted.value' => 1 + ]), 'code'); } } \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/controller/GoodsCate.php b/plugs/think-plugs-points-mall/src/controller/GoodsCate.php new file mode 100644 index 0000000..4e97de6 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/controller/GoodsCate.php @@ -0,0 +1,114 @@ +get)->layTable(function () { + $this->title = "商品分类管理"; + }, static function (QueryHelper $query) { + $query->where(['deleted' => 0]); + $query->like('name')->equal('status')->dateBetween('create_time'); + }); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _page_filter(array &$data) + { + $data = DataExtend::arr2table($data); + } + + /** + * 添加商品分类 + * @auth true + */ + public function add() + { + PointsMallGoodsCate::mForm('form'); + } + + /** + * 编辑商品分类 + * @auth true + */ + public function edit() + { + PointsMallGoodsCate::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if ($this->request->isGet()) { + $data['pid'] = intval($data['pid'] ?? input('pid', '0')); + $this->cates = PointsMallGoodsCate::pdata($this->maxLevel, $data, [ + 'id' => '0', 'pid' => '-1', 'name' => '顶部分类', + ]); + } + } + + /** + * 修改商品分类状态 + * @auth true + */ + public function state() + { + PointsMallGoodsCate::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除商品分类 + * @auth true + */ + public function remove() + { + PointsMallGoodsCate::mDelete(); + } + + /** + * 商品分类选择器 + * @login true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function select() + { + $this->get['status'] = 1; + $this->get['deleted'] = 0; + $this->index(); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/controller/Order.php b/plugs/think-plugs-points-mall/src/controller/Order.php new file mode 100644 index 0000000..051ff2a --- /dev/null +++ b/plugs/think-plugs-points-mall/src/controller/Order.php @@ -0,0 +1,138 @@ +type = trim($this->get['type'] ?? 'ta', 't'); + PointsMallOrder::mQuery()->layTable(function (QueryHelper $query) { + $this->title = '订单数据管理'; + $this->total = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 't7' => 0, 'ta' => 0]; + $this->types = ['ta' => '全部订单', 't2' => '待支付', 't3' => '待审核', 't4' => '待发货', 't5' => '已发货', 't6' => '已收货', 't7' => '已评论', 't0' => '已取消']; + foreach ($query->db()->field('status,count(1) total')->group('status')->cursor() as $vo) { + [$this->total["t{$vo['status']}"] = $vo['total'], $this->total['ta'] += $vo['total']]; + } + }, function (QueryHelper $query) { + + $query->with(['user', 'from', 'items', 'address']); + + $query->equal('status,refund_status')->like('order_no'); + $query->dateBetween('create_time,payment_time,cancel_time,delivery_type'); + + // 用户搜索查询 + $db = PluginAccountUser::mQuery()->like('phone|nickname#user_keys')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + + // 列表选项卡 + if (is_numeric($this->type)) { + $query->where(['status' => $this->type]); + } + + // 分页排序处理 + $query->where(['deleted_status' => 0]); + }); + } + + /** + * 单据凭证支付审核 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function audit() + { + if ($this->request->isGet()) { + PointsMallOrder::mForm('', 'order_no'); + } else { + $data = $this->_vali([ + 'order_no.require' => '订单单号不能为空!', + 'status.in:0,1' => '审核状态数值异常!', + 'status.require' => '审核状态不能为空!', + 'remark.default' => '', + ]); + if (empty($data['status'])) { + $data['status'] = 0; + $data['cancel_status'] = 1; + $data['cancel_remark'] = $data['remark'] ?: '后台审核驳回并取消订单'; + $data['cancel_time'] = date('Y-m-d H:i:s'); + } else { + $data['status'] = 4; + $data['payment_code'] = CodeExtend::uniqidDate(16, 'T'); + $data['payment_time'] = date('Y-m-d H:i:s'); + $data['payment_status'] = 1; + $data['payment_remark'] = $data['remark'] ?: '后台审核支付凭证通过'; + } + $order = PointsMallOrder::mk()->where(['order_no' => $data['order_no']])->findOrEmpty(); + if ($order->isEmpty() || $order['status'] !== 3) $this->error('不允许操作审核!'); + // 无需发货时的处理 + if ($data['status'] === 4 && empty($order['delivery_type'])) $data['status'] = 6; + // 更新订单支付状态 + $map = ['status' => 3, 'order_no' => $data['order_no']]; + if (PointsMallOrder::mk()->strict(false)->where($map)->update($data) !== false) { + if (in_array($data['status'], [4, 5, 6])) { + $this->app->event->trigger('PluginPaymentSuccess', $data); + $this->success('订单审核通过成功!'); + } else { + $this->app->event->trigger('PluginWemallOrderCancel', $order); + UserOrderService::stock($data['order_no']); + $this->success('审核驳回并取消成功!'); + } + } else { + $this->error('订单审核失败!'); + } + } + } + + /** + * 取消未支付的订单 + * @auth true + * @return void + */ + public function cancel() + { + $data = $this->_vali(['order_no.require' => '订单号不能为空!']); + $order = PointsMallOrder::mk()->where($data)->findOrEmpty(); + if ($order->isEmpty()) $this->error('订单查询异常!'); + try { + if (!in_array($order['status'], [1, 2, 3])) { + $this->error('订单不能取消!'); + } + $result = $order->save([ + 'status' => 0, + 'cancel_status' => 1, + 'cancel_remark' => '后台取消未支付的订单', + 'cancel_time' => date('Y-m-d H:i:s'), + ]); + if ($result !== false) { + UserOrderService::stock($order['order_no']); + $this->app->event->trigger('PluginWemallOrderCancel', $order); + $this->success('取消未支付的订单成功!'); + } else { + $this->error('取消支付的订单失败!'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/controller/api/Auth.php b/plugs/think-plugs-points-mall/src/controller/api/Auth.php index ed830e1..1f5ff49 100644 --- a/plugs/think-plugs-points-mall/src/controller/api/Auth.php +++ b/plugs/think-plugs-points-mall/src/controller/api/Auth.php @@ -7,6 +7,7 @@ use think\exception\HttpResponseException; class Auth extends AuthController { + protected $checkBind = false; /** * 控制器初始化 * @return void @@ -15,7 +16,7 @@ class Auth extends AuthController { try { parent::initialize(); - $this->checkUserStatus(false); + $this->checkUserStatus($this->checkBind); } catch (HttpResponseException $exception) { throw $exception; } catch (\Exception $exception) { diff --git a/plugs/think-plugs-points-mall/src/controller/api/Goods.php b/plugs/think-plugs-points-mall/src/controller/api/Goods.php new file mode 100644 index 0000000..529761e --- /dev/null +++ b/plugs/think-plugs-points-mall/src/controller/api/Goods.php @@ -0,0 +1,95 @@ +cnames = null; + PointsMallGoods::mQuery(null, function (QueryHelper $query) { + // 显示分类显示 + if (!empty($vCates = input('cates'))) { + $cates = array_filter(PointsMallGoodsCate::items(), function ($v) use ($vCates) { + return $v['id'] == $vCates; + }); + $this->cnames = null; + if (count($cates) > 0) { + $cate = array_pop($cates); + $this->cnames = array_combine($cate['ids'], $cate['names']); + } + } + $query->equal('code')->like('name#keys')->like('marks,cates', ','); + if (!empty($code = input('code'))) { + // 查询单个商品详情 + $query->with(['discount', 'items', 'comments' => function (Query $query) { + $query->limit(2)->where(['status' => 1, 'deleted' => 0]); + }])->withCount(['comments' => function (Query $query) { + $query->where(['status' => 1, 'deleted' => 0]); + }]); + PointsMallGoods::mk()->where(['code' => $code])->inc('num_read')->update([]); + } else { + $query->with('discount')->withoutField('content'); + } + // 数据排序处理 + $sort = intval(input('sort', 0)); + $type = intval(input('order', 0)) ? 'asc' : 'desc'; + if ($sort === 1) { + $query->order("num_read {$type},sort {$type},id {$type}"); + } elseif ($sort === 2) { + $query->order("price_selling {$type},sort {$type},id {$type}"); + } else { + $query->order("sort {$type},id {$type}"); + } + $query->where(['status' => 1, 'deleted' => 0]); + // 查询数据分页 + $page = intval(input('page', 1)); + $limit = max(min(intval(input('limit', 20)), 60), 1); + $this->success('获取商品数据', $query->page($page, false, false, $limit)); + }); + } + + /** + * 数据结果处理 + * @param array $data + * @param array $result + * @return void + */ + protected function _get_page_filter(array &$data, array &$result) + { + $result['cnames'] = $this->cnames ?? null; + } + + /** + * 获取商品分类及标签 + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function cate() + { + $this->success('获取分类成功', [ + 'cate' => PointsMallGoodsCate::dtree(), + ]); + } + + +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/controller/api/auth/Cart.php b/plugs/think-plugs-points-mall/src/controller/api/auth/Cart.php new file mode 100644 index 0000000..53cdd6b --- /dev/null +++ b/plugs/think-plugs-points-mall/src/controller/api/auth/Cart.php @@ -0,0 +1,67 @@ +equal('ghash')->where(['unid' => $this->unid])->with([ + 'goods' => static function (Query $query) { + $query->with('items'); + }, + 'specs' => static function (Query $query) { + $query->withoutField('id,create_time,update_time'); + }, + ]); + $this->success('获取购买车数据!', $query->order('id desc')->page(false, false)); + }); + } + + /** + * 修改购买车数据 + * @return void + * @throws \think\db\exception\DbException + */ + public function set() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'ghash.require' => '商品不能为空!', + 'number.require' => '数量不能为空!', + ]); + // 清理数量0的记录 + $map = ['unid' => $this->unid, 'ghash' => $data['ghash']]; + if ($data['number'] < 1) { + PointsMallOrderCart::mk()->where($map)->delete(); + UserActionService::recount($this->unid); + $this->success('移除成功!'); + } + // 检查商品是否存在 + $gspec = PointsMallGoodsItem::mk()->where(['ghash' => $data['ghash']])->findOrEmpty(); + $goods = PointsMallGoods::mk()->where(['code' => $gspec->getAttr('gcode')])->findOrEmpty(); + if ($goods->isEmpty() || $gspec->isEmpty()) $this->error('商品不存在!'); + // 保存商品数据 + $data += ['gcode' => $gspec['gcode'], 'gspec' => $gspec['gspec']]; + if (($cart = PointsMallOrderCart::mk()->where($map)->findOrEmpty())->save($data)) { + UserActionService::recount($this->unid); + $this->success('保存成功!', $cart->refresh()->toArray()); + } else { + $this->error('保存失败!'); + } + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/controller/api/auth/Order.php b/plugs/think-plugs-points-mall/src/controller/api/auth/Order.php new file mode 100644 index 0000000..f95fd34 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/controller/api/auth/Order.php @@ -0,0 +1,509 @@ +with('items')->where(['refund_status' => 0]); + } else { + $query->with(['items', 'address', 'sender', 'payments' => function (Query $query) { + $query->where(static function (Query $query) { +// $query->whereOr(['channel_type' => Payment::VOUCHER, 'payment_status' => 1, 'audit_status' => 1]); + $query->whereOr(['payment_status' => 1, 'audit_status' => 1]); + }); + }]); + } + $query->in('status')->equal('order_no'); + $query->where(['unid' => $this->unid, 'deleted_status' => 0])->order('id desc'); + $this->success('获取订单成功!', $query->page(intval(input('page')), false, false, 10)); + }); + } + + /** + * 创建订单数据 + * @return void + */ + public function add() + { + try { + // 请求参数检查 + $input = $this->_vali([ + 'carts.default' => '', + 'rules.default' => '', + 'agent.default' => '0', + ]); + if (empty($input['rules']) && empty($input['carts'])) $this->error('参数无效!'); + // 生成统一编号 + do $extra = ['order_no' => $order['order_no'] = CodeExtend::uniqidNumber(16, 'N')]; + while (PointsMallOrder::mk()->master()->where($extra)->findOrEmpty()->isExists()); + [$items, $deliveryType] = [[], 0]; + // 组装订单数据 + foreach (GoodsService::parse($this->unid, trim($input['rules'], ':;'), $input['carts']) as $item) { + if (empty($item['count'])) continue; + if (empty($item['goods']) || empty($item['specs'])) $this->error('商品无效!'); + [$goods, $gspec, $count] = [$item['goods'], $item['specs'], intval($item['count'])]; + // 订单物流类型 + if (empty($deliveryType) && $goods['delivery_code'] !== 'NONE') $deliveryType = 1; + // 限制购买数量 + if (isset($goods['limit_maxnum']) && $goods['limit_maxnum'] > 0) { + $join = [PointsMallOrderItem::mk()->getTable() => 'b']; + $where = [['a.unid', '=', $this->unid], ['a.status', '>', 1], ['b.gcode', '=', $goods['code']]]; + $buyCount = PointsMallOrder::mk()->alias('a')->join($join, 'a.order_no=b.order_no')->where($where)->sum('b.stock_sales'); + if ($buyCount + $count > $goods['limit_maxnum']) $this->error('商品限购!'); + } + // 限制购买身份 +// if ($goods['limit_lowvip'] > $this->levelCode) $this->error('等级不够!'); + // 商品库存检查 + if ($gspec['stock_sales'] + $count > $gspec['stock_total']) $this->error('库存不足!'); + // 订单详情处理 + $items[] = [ + 'unid' => $order['unid'], + 'order_no' => $order['order_no'], + // 商品字段 + 'gsku' => $gspec['gsku'], + 'gname' => $goods['name'], + 'gcode' => $gspec['gcode'], + 'ghash' => $gspec['ghash'], + 'gspec' => $gspec['gspec'], + 'gunit' => $gspec['gunit'], + 'gcover' => empty($gspec['gimage']) ? $goods['cover'] : $gspec['gimage'], + // 库存数量处理 + 'stock_sales' => $count, + // 快递发货数据 + 'delivery_code' => $goods['delivery_code'], + 'delivery_count' => $goods['rebate_type'] > 0 ? $gspec['number_express'] * $count : 0, + // 商品费用字段 + 'price_cost' => $gspec['price_cost'], + 'price_market' => $gspec['price_market'], + 'price_selling' => $gspec['price_selling'], + // 商品费用统计 + 'total_price_cost' => $gspec['price_cost'] * $count, + 'total_price_market' => $gspec['price_market'] * $count, + 'total_price_selling' => $gspec['price_selling'] * $count, + 'total_allow_balance' => $gspec['allow_balance'] * $count, + 'total_allow_integral' => $gspec['allow_integral'] * $count, + 'total_reward_balance' => $gspec['reward_balance'] * $count, + 'total_reward_integral' => $gspec['reward_integral'] * $count, + ]; + } + // 默认使用销售销售 + $order['rebate_amount'] = array_sum(array_column($items, 'rebate_amount')); + $order['allow_balance'] = array_sum(array_column($items, 'total_allow_balance')); + $order['allow_integral'] = array_sum(array_column($items, 'total_allow_integral')); + $order['reward_balance'] = array_sum(array_column($items, 'total_reward_balance')); + $order['reward_integral'] = array_sum(array_column($items, 'total_reward_integral')); + // 会员及代理升级 + $order['level_agent'] = intval(max(array_column($items, 'level_agent'))); + $order['level_member'] = intval(max(array_column($items, 'level_upgrade'))); + // 订单发货类型 + $order['status'] = $deliveryType ? 1 : 2; + $order['delivery_type'] = $deliveryType; + $order['ratio_integral'] = 100; + // 统计商品数量 + $order['number_goods'] = array_sum(array_column($items, 'stock_sales')); + $order['number_express'] = array_sum(array_column($items, 'delivery_count')); + // 统计商品金额 + $order['amount_cost'] = array_sum(array_column($items, 'total_price_cost')); + $order['amount_goods'] = array_sum(array_column($items, 'total_price_selling')); + // 折扣后的金额 + $order['amount_discount'] = array_sum(array_column($items, 'discount_amount')); + $order['amount_reduct'] = $order['amount_goods']; + // 统计订单金额 + $order['amount_real'] = round($order['amount_discount'] - $order['amount_reduct'], 2); + $order['amount_total'] = $order['amount_goods']; + $order['amount_profit'] = round($order['amount_real'] - $order['amount_cost']); + // 写入商品数据 + $model = PointsMallOrder::mk(); + $this->app->db->transaction(function () use ($order, $items, &$model) { + $model->save($order) && PointsMallOrderItem::mk()->saveAll($items); + // 设置收货地址 + if ($order['delivery_type']) { + $where = ['unid' => $this->unid, 'deleted' => 0]; + $address = PointsMallAddress::mk()->where($where)->order('type desc,id desc')->findOrEmpty(); + $address->isExists() && UserOrderService::perfect($model->refresh(), $address); + } + }); + // 同步库存销量 + foreach (array_unique(array_column($items, 'gcode')) as $gcode) { + GoodsService::stock($gcode); + } + // 清理购物车数据 + if (count($carts = str2arr($input['carts'])) > 0) { + PointsMallOrderCart::mk()->whereIn('id', $carts)->delete(); + UserActionService::recount($this->unid); + } + // 触发订单创建事件 + $this->app->event->trigger('PluginWemallOrderCreate', $order); + // 无需发货且无需支付,直接完成支付流程 + if ($order['status'] === 2 && empty($order['amount_real'])) { + $this->success('下单成功!', $model->toArray()); + } + // 返回处理成功数据 + $this->success('下单成功!', array_merge($order, ['items' => $items])); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error("下单失败,{$exception->getMessage()}"); + } + } + + /** + * 模拟计算运费 + * @return void + * @throws \think\admin\Exception + */ +// public function express() +// { +// $data = $this->_vali([ +// 'unid.value' => $this->unid, +// 'order_no.require' => '单号不能为空!', +// 'address_id.require' => '地址不能为空!', +// ]); +// +// // 用户收货地址 +// $map = ['unid' => $this->unid, 'id' => $data['address_id']]; +// $addr = PointsMallAddress::mk()->where($map)->findOrEmpty(); +// if ($addr->isEmpty()) $this->error('地址异常!'); +// +// // 订单状态检查 +// $map = ['unid' => $this->unid, 'order_no' => $data['order_no']]; +// $tCount = intval(PointsMallOrderItem::mk()->where($map)->sum('delivery_count')); +// +// // 根据地址计算运费 +// $map = ['status' => 1, 'deleted' => 0, 'order_no' => $data['order_no']]; +// $tCode = PointsMallOrderItem::mk()->where($map)->column('delivery_code'); +// [$amount, , , $remark] = ExpressService::amount($tCode, $addr['region_prov'], $addr['region_city'], $tCount); +// $this->success('计算运费!', ['amount' => $amount, 'remark' => $remark]); +// } + + /** + * 确认收货地址 + * @return void + * @throws \think\admin\Exception + */ + public function perfect() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'order_no.require' => '单号不能为空', + 'address_id.require' => '地址不能为空', + ]); + + // 用户收货地址 + $where = ['id' => $data['address_id'], 'unid' => $this->unid, 'deleted' => 0]; + $address = PointsMallAddress::mk()->where($where)->findOrEmpty(); + if ($address->isEmpty()) $this->error('地址异常!'); + + // 订单状态检查 + $where = ['unid' => $this->unid, 'order_no' => $data['order_no'], 'delivery_type' => 1]; + $order = PointsMallOrder::mk()->where($where)->whereIn('status', [1, 2])->findOrEmpty(); + if ($order->isEmpty()) $this->error('不能修改!'); + + // 更新订单收货地址 + if (UserOrderService::perfect($order, $address)) { + $this->success('确认成功!', $order->refresh()->toArray()); + } else { + $this->error('确认失败!'); + } + } + + /** + * 获取支付通道 + * @return void + * @throws \think\admin\Exception + */ +// public function channel() +// { +// $this->success('获取支付通道!', [ +// 'toratio' => Integral::ratio(), +// 'channels' => Payment::typesByAccess($this->type, true), +// ]); +// } + + /** + * 获取支付参数 + * @return void + */ + public function payment() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'balance.default' => '0.00', + 'integral.default' => '0', + 'order_no.require' => '单号不能为空', + 'order_ps.default' => '', + 'coupon_code.default' => '', # 优惠券编号 + 'channel_code.require' => '支付不能为空', + 'payment_back.default' => '', # 支付回跳地址 + 'payment_image.default' => '', # 支付凭证图片 + ]); + try { + $order = $this->getOrderModel(); + $status = intval($order->getAttr('status')); + if ($status > 3) $this->success('已完成支付!'); + if ($status === 3) $this->error('凭证待审核!'); + if ($status !== 2) $this->error('不能发起支付!'); + + // 订单备注内容更新 + empty($data['order_ps']) || $order->save(['order_ps' => $data['order_ps']]); + + // 无需支付,直接完成订单 + if (floatval($orderAmount = $order->getAttr('amount_real')) <= 0) { + $order->save(['status' => 4]); + $this->success('已支付成功!', []); + } + + $leaveAmount = $orderAmount; + // 剩余支付金额 +// if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) { +// $this->success('已完成支付!', PaymentResponse::mk(true, '已完成支付!')->toArray()); +// } + + // 扣除优惠券 +// if (!empty($data['coupon_code']) && $data['coupon_code'] !== $order->getAttr('coupon_code')) try { +// // 检查优惠券是否有效 +//// $where = ['unid' => $this->unid, 'status' => 1, 'deleted' => 0]; +//// $coupon = PluginWemallUserCoupon::mk()->where($where)->with('bindCoupon')->findOrEmpty(); +//// if ($coupon->isEmpty() || empty($coupon->getAttr('coupon_status')) || $coupon->getAttr('coupon_deleted') > 0) { +//// $this->error('无限优惠券!'); +//// } +//// if ($coupon->getAttr('expire') > 0 && $coupon->getAttr('expire') < time()) $this->error('优惠券无效!'); +//// if (floatval($coupon->getAttr('limit_amount')) <= $orderAmount) $this->error('未达到使用条件!'); +//// [$couponCode, $couponAmount] = [strval($coupon->getAttr('code')), strval($coupon->getAttr('coupon_amount'))]; +//// $response = Payment::mk(Payment::COUPON)->create($this->account, $data['order_no'], '优惠券抵扣', $orderAmount, $couponAmount, '', '', '', $couponCode); +//// $order->save(['coupon_code' => $couponCode, 'coupon_amount' => $couponAmount]); +//// $coupon->save(['used' => 1, 'status' => 2, 'used_time' => date('Y-m-d H:i:s')]); +//// if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) $this->success('已完成支付!', $response->toArray()); +// } catch (HttpResponseException $exception) { +// throw $exception; +// } catch (\Exception $exception) { +// $this->error($exception->getMessage()); +// } + + // 积分抵扣处理 +// if ($leaveAmount > 0 && $data['integral'] > 0) { +// if ($data['integral'] > $order->getAttr('allow_integral')) $this->error("超出积分抵扣!"); +// if ($data['integral'] > Integral::recount($this->unid)['usable']) $this->error('账号积分不足!'); +// $response = Payment::mk(payment::INTEGRAL)->create($this->account, $data['order_no'], '账号积分抵扣', $orderAmount, $data['integral']); +// if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) $this->success('已完成支付!', $response->toArray()); +// } + $userPoint = UserPointService::getUserPoint($this->unid); + if ($userPoint['point'] < $leaveAmount) { + $this->error("积分不足!"); + } else { + $order->save(['status' => 4]); + UserPointService::addUserPoint($this->unid, -$leaveAmount, '积分购买商品抵扣'); + $this->success('积分抵扣成功!', []); + } + + // 余额支付扣减 +// if ($leaveAmount > 0 && $data['balance'] > 0) { +// if ($data['balance'] > $order->getAttr('allow_balance')) $this->error("超出余额限额!"); +// if ($data['balance'] > Balance::recount($this->unid)['usable']) $this->error('账号余额不足!'); +// $response = Payment::mk(Payment::BALANCE)->create($this->account, $data['order_no'], '账号余额支付!', $orderAmount, $data['balance']); +// if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) $this->success('已完成支付!', $response->toArray()); +// } + + // 凭证图片保存 +// if (!empty($data['payment_image'])) { +// $data['payment_image'] = Storage::saveImage($data['payment_image'])['url'] ?? ''; +// } + + // 创建支付订单 +// $response = Payment::mk($data['channel_code'])->create( +// $this->account, $data['order_no'], '商城订单支付', +// $orderAmount, strval($leaveAmount), '', $data['payment_back'], $data['payment_image'] +// ); + + // 标准化返回结果 +// if ($response->status) { +// $this->success($response->message, $response->toArray()); +// } else { +// $this->error($response->message, $response->toArray()); +// } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } + + /** + * 取消未支付订单 + * @return void + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function cancel() + { + $order = $this->getOrderModel(); + if (in_array($order->getAttr('status'), [1, 2, 3])) { + $data = [ + 'status' => 0, + 'cancel_time' => date('Y-m-d H:i:s'), + 'cancel_status' => 1, + 'cancel_remark' => '用户主动取消订单!', + ]; + if ($order->save($data) && UserOrderService::stock($order->getAttr('order_no'))) { + // 触发订单取消事件 + Payment::refund($order->getAttr('order_no')); + $this->app->event->trigger('PluginWemallOrderCancel', $order); + // 返回处理成功数据 + $this->success('取消成功!'); + } else { + $this->error('取消失败!'); + } + } else { + $this->error('不可取消!'); + } + } + + /** + * 删除已取消订单 + * @return void + */ + public function remove() + { + $order = $this->getOrderModel(); + if ($order->isEmpty()) $this->error('读取订单失败!'); + if ($order->getAttr('status') == 0) { + if ($order->save([ + 'status' => 0, + 'deleted_time' => date('Y-m-d H:i:s'), + 'deleted_status' => 1, + 'deleted_remark' => '用户主动删除订单!', + ])) { + // 触发订单删除事件 + $this->app->event->trigger('PluginWemallOrderRemove', $order); + // 返回处理成功数据 + $this->success('删除成功!'); + } else { + $this->error('删除失败!'); + } + } else { + $this->error('不可删除!'); + } + } + + /** + * 订单确认收货 + * @return void + */ + public function confirm() + { + $order = $this->getOrderModel(); + if (in_array($order->getAttr('status'), [5, 6])) { + // 触发订单确认事件 + $order->save([ + 'status' => 6, + 'confirm_time' => date('Y-m-d H:i:s'), + 'confirm_remark' => '用户主动确认签收订单!', + ]); + $this->app->event->trigger('PluginWemallOrderConfirm', $order); + // 返回处理成功数据 + $this->success('确认成功!'); + } else { + $this->error('确认失败!'); + } + } + + /** + * 提交订单评论 + * @return void + */ +// public function comment() +// { +// $order = $this->getOrderModel(); +// if (in_array($order->getAttr('status'), [6, 7])) try { +// // 接收评论数据 +// $items = json_decode($this->request->post('data'), true); +// $this->app->db->transaction(function () use ($order, $items) { +// $order->save(['status' => 7]); +// $order->items()->select()->map(function (PointsMallOrderItem $item) use ($items) { +// if (!empty($input = $items[$item->getAttr('ghash')])) { +// UserAction::comment($item, $input['rate'], $input['content'], $input['images']); +// } +// }); +// }); +// $this->success('评论成功!'); +// } catch (HttpResponseException $exception) { +// throw $exception; +// } catch (\Exception $exception) { +// $this->error($exception->getMessage()); +// } else { +// $this->error('无需评论!'); +// } +// } + + /** + * 订单状态统计 + * @return void + */ + public function total() + { + $data = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 't7' => 0]; + $query = PointsMallOrder::mk()->where(['unid' => $this->unid, 'refund_status' => 0, 'deleted_status' => 0]); + foreach ($query->field('status,count(1) count')->group('status')->cursor() as $item) { + $data["t{$item['status']}"] = $item['count']; + } + $this->success('获取统计!', $data); + } + + /** + * 物流追踪查询 + * @return void + */ +// public function track() +// { +// try { +// $data = $this->_vali([ +// 'code.require' => '快递不能为空', 'number.require' => '单号不能为空' +// ]); +// $result = ExpressService::query($data['code'], $data['number']); +// empty($result['code']) ? $this->error($result['info']) : $this->success('快递追踪!', $result); +// } catch (HttpResponseException $exception) { +// throw $exception; +// } catch (\Exception $exception) { +// $this->error($exception->getMessage()); +// } +// } + + /** + * 获取输入订单模型 + * @return PointsMallOrder + */ + private function getOrderModel(): PointsMallOrder + { + $map = $this->_vali(['unid.value' => $this->unid, 'order_no.require' => '单号不能为空']); + $order = PointsMallOrder::mk()->where($map)->findOrEmpty(); + if ($order->isEmpty()) $this->error('读取订单失败!'); + return $order; + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/helper.php b/plugs/think-plugs-points-mall/src/helper.php new file mode 100644 index 0000000..f1bc734 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/helper.php @@ -0,0 +1,46 @@ +hasMany(PointsMallGoodsItem::class, 'gcode', 'code') + ->withoutField('id,status,create_time,update_time') + ->where(['status' => 1]); + } + + /** + * 关联商品评论数据 + * @return \think\model\relation\HasMany + */ + public function comments() + { + return $this->hasMany(PluginWemallUserActionComment::class, 'gcode', 'code')->with('bindUser'); + } + + /** + * 关联产品列表 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function lists(): array + { + $model = static::mk()->with('items')->withoutField('specs'); + return $model->order('sort desc,id desc')->where(['deleted' => 0])->select()->toArray(); + } + + /** + * 处理商品分类数据 + * @param mixed $value + * @return array + */ + public function getCatesAttr($value): array + { + $ckey = 'PointsMallGoodsCateItem'; + $cates = sysvar($ckey) ?: sysvar($ckey, PointsMallGoodsCate::items(true)); + $cateids = is_string($value) ? str2arr($value) : (array)$value; + foreach ($cates as $cate) if (in_array($cate['id'], $cateids)) return $cate; + return []; + } + + /** + * 设置轮播图片 + * @param mixed $value + * @return string + */ + public function setSliderAttr($value): string + { + return is_string($value) ? $value : (is_array($value) ? arr2str($value) : ''); + } + + /** + * 获取轮播图片 + * @param mixed $value + * @return array + */ + public function getSliderAttr($value): array + { + return is_string($value) ? str2arr($value, '|') : []; + } + +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/model/PointsMallGoodsCate.php b/plugs/think-plugs-points-mall/src/model/PointsMallGoodsCate.php new file mode 100644 index 0000000..5d6c976 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/model/PointsMallGoodsCate.php @@ -0,0 +1,75 @@ +where(['deleted' => 0])->order('sort desc,id asc')->select()->toArray(); + $cates = DataExtend::arr2table(empty($parent) ? $items : array_merge([$parent], $items)); + if (isset($data['id'])) foreach ($cates as $cate) if ($cate['id'] === $data['id']) $data = $cate; + foreach ($cates as $key => $cate) { + $isSelf = isset($data['spt']) && isset($data['spc']) && $data['spt'] <= $cate['spt'] && $data['spc'] > 0; + if ($cate['spt'] >= $max || $isSelf) unset($cates[$key]); + } + return $cates; + } + + /** + * 获取数据树 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function dtree(): array + { + $query = static::mk()->where(['status' => 1, 'deleted' => 0])->order('sort desc,id desc'); + return DataExtend::arr2tree($query->withoutField('sort,status,deleted,create_time')->select()->toArray()); + } + + /** + * 获取列表数据 + * @param bool $simple 仅子级别 + * @return array + */ + public static function items(bool $simple = false): array + { + $query = static::mk()->where(['status' => 1, 'deleted' => 0])->order('sort desc,id asc'); + $cates = array_column(DataExtend::arr2table($query->column('id,pid,name', 'id')), null, 'id'); + foreach ($cates as $cate) isset($cates[$cate['pid']]) && $cates[$cate['id']]['parent'] =& $cates[$cate['pid']]; + foreach ($cates as $key => $cate) { + $id = $cate['id']; + $cates[$id]['ids'][] = $cate['id']; + $cates[$id]['names'][] = $cate['name']; + while (isset($cate['parent']) && ($cate = $cate['parent'])) { + $cates[$id]['ids'][] = $cate['id']; + $cates[$id]['names'][] = $cate['name']; + } + $cates[$id]['ids'] = array_reverse($cates[$id]['ids']); + $cates[$id]['names'] = array_reverse($cates[$id]['names']); + if (isset($pky) && $simple && in_array($cates[$pky]['name'], $cates[$id]['names'])) { + unset($cates[$pky]); + } + $pky = $key; + } + foreach ($cates as &$cate) { + unset($cate['sps'], $cate['parent']); + } + return array_values($cates); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/model/PointsMallGoodsItem.php b/plugs/think-plugs-points-mall/src/model/PointsMallGoodsItem.php new file mode 100644 index 0000000..83acb6b --- /dev/null +++ b/plugs/think-plugs-points-mall/src/model/PointsMallGoodsItem.php @@ -0,0 +1,67 @@ +hasOne(PointsMallGoods::class, 'code', 'gcode'); + } + + /** + * 绑定商品信息 + * @return \think\model\relation\HasOne + */ + public function bindGoods() + { + return $this->goods()->bind([ + 'gname' => 'name', + 'gcover' => 'cover', + 'gstatus' => 'status', + 'gdeleted' => 'deleted' + ]); + } + + /** + * 获取商品规格JSON数据 + * @param string $code + * @return string + */ + public static function itemsJson(string $code): string + { + return json_encode(self::itemsArray($code), 64 | 256); + } + + /** + * 获取商品规格数组 + * @param string $code + * @return array + */ + public static function itemsArray(string $code): array + { + return self::mk()->where(['gcode' => $code])->column([ + 'gsku' => 'gsku', + 'ghash' => 'hash', + 'gspec' => 'spec', + 'gcode' => 'gcode', + 'gimage' => 'image', + 'status' => 'status', + 'price_cost' => 'cost', + 'price_market' => 'market', + 'price_selling' => 'selling', + 'allow_balance' => 'allow_balance', + 'allow_integral' => 'allow_integral', + 'number_virtual' => 'virtual', + 'number_express' => 'express', + 'reward_balance' => 'balance', + 'reward_integral' => 'integral', + ], 'ghash'); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/model/PointsMallGoodsStock.php b/plugs/think-plugs-points-mall/src/model/PointsMallGoodsStock.php new file mode 100644 index 0000000..01f653d --- /dev/null +++ b/plugs/think-plugs-points-mall/src/model/PointsMallGoodsStock.php @@ -0,0 +1,10 @@ +hasOne(PluginAccountUser::class, 'id', 'puid1'); + } + + /** + * 关联商品数据 + * @return \think\model\relation\HasMany + */ + public function items() + { + return $this->hasMany(PointsMallOrderItem::class, 'order_no', 'order_no'); + } +// +// /** +// * 关联支付数据 +// * @return \think\model\relation\HasOne +// */ +// public function payment() +// { +// return $this->hasOne(PluginPaymentRecord::class, 'order_no', 'order_no')->where([ +// 'payment_status' => 1, +// ]); +// } +// +// /** +// * 关联支付记录 +// * @return \think\model\relation\HasMany +// */ +// public function payments() +// { +// return $this->hasMany(PluginPaymentRecord::class, 'order_no', 'order_no')->order('id desc')->withoutField('payment_notify'); +// } +// +// /** +// * 关联收货地址 +// * @return \think\model\relation\HasOne +// */ +// public function address() +// { +// return $this->hasOne(PluginWemallOrderSender::class, 'order_no', 'order_no'); +// } + + /** + * 格式化支付通道 + * @param mixed $value + * @return array + */ + public function getPaymentAllowsAttr($value): array + { + $payments = is_string($value) ? str2arr($value) : []; + return in_array('all', $payments) ? ['all'] : $payments; + } + + /** + * 时间格式处理 + * @param mixed $value + * @return string + */ + public function getPaymentTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } + + /** + * 时间格式处理 + * @param mixed $value + * @return string + */ + public function setPaymentTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + public function setConfirmTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + public function getConfirmTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/model/PointsMallOrderCart.php b/plugs/think-plugs-points-mall/src/model/PointsMallOrderCart.php new file mode 100644 index 0000000..6e8cae5 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/model/PointsMallOrderCart.php @@ -0,0 +1,26 @@ +hasOne(PointsMallGoods::class, 'code', 'gcode'); + } + + /** + * 关联规格数据 + * @return \think\model\relation\HasOne + */ + public function specs() + { + return $this->hasOne(PointsMallGoodsItem::class, 'ghash', 'ghash'); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/model/PointsMallOrderItem.php b/plugs/think-plugs-points-mall/src/model/PointsMallOrderItem.php new file mode 100644 index 0000000..3a9a1d9 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/model/PointsMallOrderItem.php @@ -0,0 +1,26 @@ +hasOne(PointsMallOrder::class, 'order_no', 'order_no'); + } + + /** + * 关联商品信息 + * @return \think\model\relation\HasOne + */ + public function goods() + { + return $this->hasOne(PointsMallGoods::class, 'code', 'gcode'); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/model/PointsMallOrderSender.php b/plugs/think-plugs-points-mall/src/model/PointsMallOrderSender.php new file mode 100644 index 0000000..4e42081 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/model/PointsMallOrderSender.php @@ -0,0 +1,38 @@ +hasOne(PointsMallOrder::class, 'order_no', 'order_no')->with(['items']); + } + + /** + * 设置发货时间 + * @param mixed $value + * @return string + */ + public function setExpressTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + /** + * 获取发货时间 + * @param mixed $value + * @return string + */ + public function getExpressTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/service/GoodsService.php b/plugs/think-plugs-points-mall/src/service/GoodsService.php new file mode 100644 index 0000000..b62d379 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/service/GoodsService.php @@ -0,0 +1,122 @@ +field('ghash,ifnull(sum(gstock),0) stock_total'); + $stockList = $query->where(['gcode' => $code])->group('gcode,ghash')->select()->toArray(); + // 销量统计 + $query = PointsMallOrder::mk()->alias('a')->field('b.ghash,ifnull(sum(b.stock_sales),0) stock_sales'); + $query->join([PointsMallOrderItem::mk()->getTable() => 'b'], 'a.order_no=b.order_no', 'left'); + $query->where([['b.gcode', '=', $code], ['a.status', '>', 0], ['a.deleted_status', '=', 0]]); + $salesList = $query->group('b.ghash')->select()->toArray(); + // 组装数据 + $items = []; + foreach (array_merge($stockList, $salesList) as $vo) { + $key = $vo['ghash']; + $items[$key] = array_merge($items[$key] ?? [], $vo); + if (empty($items[$key]['stock_sales'])) $items[$key]['stock_sales'] = 0; + if (empty($items[$key]['stock_total'])) $items[$key]['stock_total'] = 0; + } + unset($salesList, $stockList); + // 更新商品规格销量及库存 + foreach ($items as $hash => $item) { + PointsMallGoodsItem::mk()->where(['ghash' => $hash])->update([ + 'stock_total' => $item['stock_total'], 'stock_sales' => $item['stock_sales'] + ]); + } + // 更新商品主体销量及库存 + PointsMallGoods::mk()->where(['code' => $code])->update([ + 'stock_total' => intval(array_sum(array_column($items, 'stock_total'))), + 'stock_sales' => intval(array_sum(array_column($items, 'stock_sales'))), + 'stock_virtual' => PointsMallGoodsItem::mk()->where(['gcode' => $code])->sum('number_virtual'), + ]); + return true; + } + + /** + * 解析下单数据 + * @param integer $unid 用户编号 + * @param string $rules 直接下单 + * @param string $carts 购物车下单 + * @return array + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function parse(int $unid, string $rules, string $carts): array + { + // 读取商品数据 + [$lines, $carts] = [[], str2arr($carts)]; + if (!empty($carts)) { + $where = [['unid', '=', $unid], ['id', 'in', $carts]]; + $field = ['ghash' => 'ghash', 'gcode' => 'gcode', 'gspec' => 'gspec', 'number' => 'count']; + PointsMallOrderCart::mk()->field($field)->where($where)->with([ + 'goods' => function ($query) { + $query->where(['status' => 1, 'deleted' => 0]); + $query->withoutField(['specs', 'content', 'status', 'deleted', 'create_time', 'update_time']); + }, + 'specs' => function ($query) { + $query->where(['status' => 1]); + $query->withoutField(['status', 'create_time', 'update_time']); + } + ])->select()->each(function (Model $model) use (&$lines) { + if (isset($lines[$ghash = $model->getAttr('ghash')])) { + $lines[$ghash]['count'] += $model->getAttr('count'); + } else { + $lines[$ghash] = $model->toArray(); + } + }); + } elseif (!empty($rules)) { + foreach (explode(';', $rules) as $rule) { + [$ghash, $count] = explode(':', "{$rule}:1"); + if (isset($lines[$ghash])) { + $lines[$ghash]['count'] += $count; + } else { + $lines[$ghash] = ['ghash' => $ghash, 'gcode' => '', 'gspec' => '', 'count' => $count]; + } + } + // 读取规格数据 + $map1 = [['status', '=', 1], ['ghash', 'in', array_column($lines, 'ghash')]]; + foreach (PointsMallGoodsItem::mk()->where($map1)->select()->toArray() as $item) { + foreach ($lines as &$line) if ($line['ghash'] === $item['ghash']) { + [$line['gcode'], $line['gspec'], $line['specs']] = [$item['gcode'], $item['gspec'], $item]; + } + } + // 读取商品数据 + $map2 = [['status', '=', 1], ['deleted', '=', 0], ['code', 'in', array_unique(array_column($lines, 'gcode'))]]; + foreach (PointsMallGoods::mk()->where($map2)->withoutField(['specs', 'content'])->select()->toArray() as $goods) { + foreach ($lines as &$line) if ($line['gcode'] === $goods['code']) $line['goods'] = $goods; + } + } else { + throw new Exception('无效参数数据!'); + } + return array_values($lines); + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/service/UserActionService.php b/plugs/think-plugs-points-mall/src/service/UserActionService.php new file mode 100644 index 0000000..bcb7a0f --- /dev/null +++ b/plugs/think-plugs-points-mall/src/service/UserActionService.php @@ -0,0 +1,29 @@ + $unid]; + $data['mycarts_total'] = PointsMallOrderCart::mk()->where($map)->sum('number'); + if ($isUpdate && ($user = PluginAccountUser::mk()->findOrEmpty($unid))->isExists()) { + $user->save(['extra' => array_merge($user->getAttr('extra'), $data)]); + } + return [$data['collect_total'], $data['history_total'], $data['mycarts_total']]; + } +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/service/UserOrderService.php b/plugs/think-plugs-points-mall/src/service/UserOrderService.php new file mode 100644 index 0000000..705b3fd --- /dev/null +++ b/plugs/think-plugs-points-mall/src/service/UserOrderService.php @@ -0,0 +1,152 @@ + $orderNo]; + $codes = PointsMallOrderItem::mk()->where($map)->column('gcode'); + foreach (array_unique($codes) as $code) GoodsService::stock($code); + return true; + } + + /** + * 获取订单模型 + * @param PointsMallOrder|string $order + * @param ?integer $unid 动态绑定变量 + * @param ?string $orderNo 动态绑定变量 + * @return PointsMallOrder + * @throws \think\admin\Exception + */ + public static function widthOrder($order, ?int &$unid = 0, ?string &$orderNo = '') + { + if (is_string($order)) { + $order = PointsMallOrder::mk()->where(['order_no' => $order])->findOrEmpty(); + } + if ($order instanceof PointsMallOrder) { + [$unid, $orderNo] = [intval($order->getAttr('unid')), $order->getAttr('order_no')]; + return $order; + } + throw new Exception("无效订单对象!"); + } + + /** + * 更新订单支付状态 + * @param PluginWemallOrder|string $order 订单模型 + * @param PluginPaymentRecord $payment 支付行为记录 + * @return array|bool|string|void|null + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + * @remark 订单状态(0已取消,1预订单,2待支付,3待审核,4待发货,5已发货,6已收货,7已评论) + */ + public static function change($order, PluginPaymentRecord $payment) + { + $order = self::widthOrder($order); + if ($order->isEmpty()) return null; + + // 同步订单支付统计 + $ptotal = Payment::totalPaymentAmount($payment->getAttr('order_no')); + $order->appendData([ + 'payment_time' => $payment->getAttr('create_time'), + 'payment_amount' => $ptotal['amount'] ?? 0, + 'amount_payment' => $ptotal['payment'] ?? 0, + 'amount_balance' => $ptotal['balance'] ?? 0, + 'amount_integral' => $ptotal['integral'] ?? 0, + ], true); + + // 订单已经支付完成 + if ($order->getAttr('payment_amount') >= $order->getAttr('amount_real')) { + // 已完成支付,更新订单状态 + $status = $order->getAttr('delivery_type') ? 4 : 5; + $order->save(['status' => $status, 'payment_status' => 1]); + // 确认完成支付,发放余额积分奖励及升级返佣 + } + + // 退款或部分退款,仅更新订单支付统计 + if ($payment->getAttr('refund_status')) { + return $order->save(); + } + + // 提交支付凭证,只需更新订单状态为【待审核】 + $isVoucher = $payment->getAttr('channel_type') === Payment::VOUCHER; + if ($isVoucher && $payment->getAttr('audit_status') === 1) { + return $order->save(['status' => 3, 'payment_status' => 1]); + } + + // 凭证支付审核被拒绝,订单回滚到未支付状态 + if ($isVoucher && $payment->getAttr('audit_status') === 0) { + if ($order->getAttr('status') === 3) $order->save(['status' => 2]); + } else { + $order->save(); + } + } + + /** + * 更新订单收货地址 + * @param PointsMallOrder $order + * @param $address + * @return boolean + * @throws \think\admin\Exception + */ + public static function perfect(PointsMallOrder $order, $address): bool + { + $unid = $order->getAttr('unid'); + $orderNo = $order->getAttr('order_no'); + // 创建订单发货信息 + $data = [ + 'delivery_code' => 0, 'delivery_count' => 0, 'unid' => $unid, + 'delivery_remark' => '', 'delivery_amount' => 0, 'status' => 1, + ]; + $data['order_no'] = $orderNo; + $data['address_id'] = $address->getAttr('id'); + // 收货人信息 + $data['user_name'] = $address->getAttr('user_name'); + $data['user_phone'] = $address->getAttr('user_phone'); + $data['user_idcode'] = $address->getAttr('idcode'); + $data['user_idimg1'] = $address->getAttr('idimg1'); + $data['user_idimg2'] = $address->getAttr('idimg2'); + // 收货地址信息 + $data['region_prov'] = $address->getAttr('region_prov'); + $data['region_city'] = $address->getAttr('region_city'); + $data['region_area'] = $address->getAttr('region_area'); + $data['region_addr'] = $address->getAttr('region_addr'); + // 记录原地址信息 + $data['extra'] = $data; + PointsMallOrderSender::mk()->where(['order_no' => $orderNo])->findOrEmpty()->save($data); + // 组装更新订单数据, 重新计算订单金额 + $update = ['status' => 2, 'amount_express' => $data['delivery_amount']]; + $update['amount_real'] = round($order->getAttr('amount_discount') - $order->getAttr('amount_reduct'), 2); + $update['amount_total'] = round($order->getAttr('amount_goods'), 2); + // 支付金额不能少于零 + if ($update['amount_real'] <= 0) $update['amount_real'] = 0.00; + if ($update['amount_total'] <= 0) $update['amount_total'] = 0.00; + // 更新用户订单数据 + if ($order->save($update)) { + // 触发订单确认事件 + Library::$sapp->event->trigger('PluginWemallOrderPerfect', $order); + // 返回处理成功数据 + return true; + } else { + return false; + } + } + +} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/view/goods/form.html b/plugs/think-plugs-points-mall/src/view/goods/form.html new file mode 100644 index 0000000..0835bca --- /dev/null +++ b/plugs/think-plugs-points-mall/src/view/goods/form.html @@ -0,0 +1,349 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name="content"} +{include file='goods/form_style'} +
+{/block} + +{block name='script'} + + + +{/block} \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/view/goods/form_style.html b/plugs/think-plugs-points-mall/src/view/goods/form_style.html new file mode 100644 index 0000000..52fa8b6 --- /dev/null +++ b/plugs/think-plugs-points-mall/src/view/goods/form_style.html @@ -0,0 +1,185 @@ + \ No newline at end of file diff --git a/plugs/think-plugs-points-mall/src/view/goods/index.html b/plugs/think-plugs-points-mall/src/view/goods/index.html new file mode 100644 index 0000000..f72101f --- /dev/null +++ b/plugs/think-plugs-points-mall/src/view/goods/index.html @@ -0,0 +1,113 @@ +{extend name="table"} + +{block name="button"} + + + + + + + + +{/block} + +{block name="content"} +