首页/ phph5/ 甜趣蛋糕 · 烘焙蛋糕店 PHP+H5 商城 + 商家后台/ 代码预览

💻 代码预览 · 甜趣蛋糕 · 烘焙蛋糕店 PHP+H5 商城 + 商家后台

ph_mall · 上传者 驰旭教育
← 返回详情
r117/app/controllers/ApiController.php · 约 20957 字符
1<?php2/**3 * 业务 API(H5 前端调用)4 * 所有输出统一 {ok:1, ...} / {ok:0, msg}5 */6class ApiController extends Controller {7    private function imgArr($s) {8        return array_values(array_filter(explode('|', (string)$s), fn($x) => trim($x) !== ''));9    }1011    // 首页轮播12    public function banners() {13        $list = (new BannerModel())->all();14        $data = array_map(fn($b) => [15            'id' => $b['id'], 'title' => $b['title'], 'image' => $b['image'], 'link' => $b['link']16        ], $list);17        $this->json(['ok' => 1, 'data' => $data]);18    }1920    // 分类:场景 / 品类 / 尺寸21    public function categories() {22        $cats = (new CategoryModel())->all();23        $scenes = []; $kinds = []; $sizes = [];24        foreach ($cats as $c) {25            if ($c['type'] === 'scene') $scenes[] = $c['name'];26            elseif ($c['type'] === 'kind') $kinds[] = ['id' => $c['id'], 'name' => $c['name']];27            elseif ($c['type'] === 'size') $sizes[] = $c['name'];28        }29        $this->json(['ok' => 1, 'scenes' => $scenes, 'kinds' => $kinds, 'sizes' => $sizes]);30    }3132    // 商品列表(支持 品类/场景/关键词/标签/排序 过滤)33    public function products() {34        $kind = (int)$this->input('kind', 0);35        $scene = trim((string)$this->input('scene', ''));36        $kw = trim((string)$this->input('kw', ''));37        $tag = trim((string)$this->input('tag', ''));38        $sort = trim((string)$this->input('sort', 'default'));39        $sql = "SELECT * FROM products WHERE status=1";40        $params = [];41        if ($kind) { $sql .= " AND kind_id=?"; $params[] = $kind; }42        if ($scene) { $sql .= " AND scenes LIKE ?"; $params[] = "%$scene%"; }43        if ($kw) { $sql .= " AND (name LIKE ? OR tags LIKE ?)"; $params[] = "%$kw%"; $params[] = "%$kw%"; }44        if ($tag) { $sql .= " AND $tag=1"; }45        if ($sort === 'sales') $sql .= " ORDER BY sales DESC";46        elseif ($sort === 'price_asc') $sql .= " ORDER BY price ASC";47        elseif ($sort === 'price_desc') $sql .= " ORDER BY price DESC";48        elseif ($sort === 'rating') $sql .= " ORDER BY rating DESC";49        else $sql .= " ORDER BY is_hot DESC, sales DESC";50        $st = $this->db->prepare($sql);51        $st->execute($params);52        $rows = $st->fetchAll();53        $data = array_map(function ($p) {54            return [55                'id' => $p['id'], 'name' => $p['name'], 'price' => (float)$p['price'],56                'original_price' => (float)$p['original_price'], 'sales' => (int)$p['sales'],57                'rating' => (float)$p['rating'], 'stock' => (int)$p['stock'],58                'is_new' => (int)$p['is_new'], 'is_hot' => (int)$p['is_hot'],59                'is_recommend' => (int)$p['is_recommend'], 'tags' => $p['tags'],60                'images' => $this->imgArr($p['images'])61            ];62        }, $rows);63        $this->json(['ok' => 1, 'data' => $data]);64    }6566    // 商品详情(含 SKU 与 评价)67    public function product_detail() {68        $id = (int)$this->input('id', 0);69        $p = (new ProductModel())->find($id);70        if (!$p) $this->json(['ok' => 0, 'msg' => '商品不存在'], 404);71        $skus = (new SkuModel())->all(['product_id' => $id]);72        $skus = array_map(fn($s) => [73            'id' => $s['id'], 'size' => $s['size'], 'flavor' => $s['flavor'],74            'price' => (float)$s['price'], 'stock' => (int)$s['stock']75        ], $skus);76        $revs = $this->db->prepare("SELECT r.*, m.username FROM reviews r LEFT JOIN members m ON m.id=r.member_id WHERE r.product_id=? AND r.status=1 ORDER BY r.id DESC");77        $revs->execute([$id]);78        $reviews = array_map(fn($r) => [79            'id' => $r['id'], 'rating' => (int)$r['rating'], 'content' => $r['content'],80            'username' => $r['username'] ?: '匿名', 'created_at' => $r['created_at'],81            'images' => $this->imgArr($r['images']), 'reply' => $r['reply']82        ], $revs->fetchAll());83        $this->json(['ok' => 1, 'product' => [84            'id' => $p['id'], 'name' => $p['name'], 'price' => (float)$p['price'],85            'original_price' => (float)$p['original_price'], 'sales' => (int)$p['sales'],86            'rating' => (float)$p['rating'], 'stock' => (int)$p['stock'],87            'is_new' => (int)$p['is_new'], 'is_hot' => (int)$p['is_hot'],88            'is_recommend' => (int)$p['is_recommend'], 'description' => $p['description'],89            'ingredients' => $p['ingredients'], 'allergen' => $p['allergen'],90            'shelf_life' => $p['shelf_life'], 'advice' => $p['advice'],91            'images' => $this->imgArr($p['images']), 'tags' => $p['tags'], 'scenes' => $p['scenes']92        ], 'skus' => $skus, 'reviews' => $reviews]);93    }9495    // 搜索96    public function search() {97        $kw = trim((string)$this->input('kw', ''));98        $st = $this->db->prepare("SELECT * FROM products WHERE status=1 AND (name LIKE ? OR tags LIKE ? OR scenes LIKE ?) ORDER BY sales DESC");99        $st->execute(["%$kw%", "%$kw%", "%$kw%"]);100        $data = array_map(fn($p) => [101            'id' => $p['id'], 'name' => $p['name'], 'price' => (float)$p['price'],102            'sales' => (int)$p['sales'], 'images' => $this->imgArr($p['images'])103        ], $st->fetchAll());104        $this->json(['ok' => 1, 'data' => $data]);105    }106107    // 推荐(热销 + 推荐)108    public function recommend() {109        $st = $this->db->prepare("SELECT * FROM products WHERE status=1 AND (is_recommend=1 OR is_hot=1) ORDER BY is_recommend DESC, sales DESC LIMIT 8");110        $st->execute();111        $data = array_map(fn($p) => [112            'id' => $p['id'], 'name' => $p['name'], 'price' => (float)$p['price'],113            'images' => $this->imgArr($p['images'])114        ], $st->fetchAll());115        $this->json(['ok' => 1, 'data' => $data]);116    }117118    // ---- 购物车 ----119    public function cart_add() {120        $member_id = (int)$this->input('member_id', 0);121        $product_id = (int)$this->input('product_id', 0);122        $sku_id = (int)$this->input('sku_id', 0);123        $qty = max(1, (int)$this->input('qty', 1));124        if (!$product_id || !$sku_id) $this->json(['ok' => 0, 'msg' => '参数缺失'], 400);125        (new CartModel())->create([126            'member_id' => $member_id, 'product_id' => $product_id, 'sku_id' => $sku_id, 'qty' => $qty127        ]);128        $this->json(['ok' => 1, 'msg' => '已加入购物车']);129    }130131    public function cart_list() {132        $member_id = (int)$this->input('member_id', 0);133        $st = $this->db->prepare("SELECT c.*, p.name AS pname, p.images, s.size, s.flavor, s.price FROM carts c134            JOIN products p ON p.id=c.product_id JOIN skus s ON s.id=c.sku_id135            WHERE c.member_id=? ORDER BY c.id DESC");136        $st->execute([$member_id]);137        $rows = $st->fetchAll();138        $data = array_map(fn($r) => [139            'id' => $r['id'], 'product_id' => $r['product_id'], 'sku_id' => $r['sku_id'],140            'qty' => (int)$r['qty'], 'name' => $r['pname'], 'images' => $this->imgArr($r['images']),141            'size' => $r['size'], 'flavor' => $r['flavor'], 'price' => (float)$r['price'],142            'subtotal' => round((float)$r['price'] * (int)$r['qty'], 2)143        ], $rows);144        $total = array_sum(array_column($data, 'subtotal'));145        $this->json(['ok' => 1, 'data' => $data, 'total' => round($total, 2)]);146    }147148    public function cart_update() {149        $id = (int)$this->input('id', 0);150        $qty = max(1, (int)$this->input('qty', 1));151        (new CartModel())->update($id, ['qty' => $qty]);152        $this->json(['ok' => 1]);153    }154155    public function cart_remove() {156        $id = (int)$this->input('id', 0);157        (new CartModel())->delete($id);158        $this->json(['ok' => 1]);159    }160161    // ---- 会员 ----162    public function member_register() {163        $username = trim((string)$this->input('username', ''));164        $phone = trim((string)$this->input('phone', ''));165        if (!$username) $this->json(['ok' => 0, 'msg' => '请填写昵称'], 400);166        $id = (new MemberModel())->create([167            'username' => $username, 'phone' => $phone, 'level' => 'normal',168            'points' => 0, 'balance' => 0, 'growth' => 0, 'status' => 1169        ]);170        $this->json(['ok' => 1, 'member_id' => $id]);171    }172173    public function member_info() {174        $member_id = (int)$this->input('member_id', 2);175        $m = (new MemberModel())->find($member_id);176        if (!$m) $this->json(['ok' => 0, 'msg' => '会员不存在'], 404);177        $levelName = ['normal' => '普通会员', 'silver' => '银卡会员', 'gold' => '金卡会员', 'diamond' => '钻石会员'][$m['level']] ?? '普通会员';178        $coupons = $this->db->prepare("SELECT COUNT(*) FROM coupon_user WHERE member_id=? AND status='unused'");179        $coupons->execute([$member_id]);180        $this->json(['ok' => 1, 'member' => [181            'id' => $m['id'], 'username' => $m['username'], 'phone' => $m['phone'],182            'level' => $m['level'], 'level_name' => $levelName,183            'points' => (int)$m['points'], 'balance' => (float)$m['balance'],184            'growth' => (int)$m['growth'], 'birthday' => $m['birthday']185        ], 'coupons_count' => (int)$coupons->fetchColumn()]);186    }187188    // ---- 优惠券 ----189    public function coupon_list() {190        $list = (new CouponModel())->all();191        $list = array_filter($list, fn($c) => (int)$c['status'] === 1);192        $data = array_map(function ($c) {193            $desc = $c['type'] === 'discount' ? ($c['discount'] * 10) . '折'194                : ($c['type'] === 'no_threshold' ? '无门槛¥' . $c['value']195                : ($c['threshold'] > 0 ? '满' . $c['threshold'] . '减' . $c['value'] : '¥' . $c['value'] . '券'));196                return ['id' => $c['id'], 'name' => $c['name'], 'type' => $c['type'],197                    'threshold' => (float)$c['threshold'], 'value' => (float)$c['value'],198                    'discount' => (float)$c['discount'], 'valid_days' => (int)$c['valid_days'],199                    'received' => (int)$c['received'], 'total' => (int)$c['total']];200        }, $list);201        $this->json(['ok' => 1, 'data' => array_values($data)]);202    }203204    public function coupon_receive() {205        $member_id = (int)$this->input('member_id', 0);206        $coupon_id = (int)$this->input('coupon_id', 0);207        $c = (new CouponModel())->find($coupon_id);208        if (!$c || (int)$c['status'] !== 1) $this->json(['ok' => 0, 'msg' => '券不存在或已下架'], 400);209        if ((int)$c['received'] >= (int)$c['total']) $this->json(['ok' => 0, 'msg' => '已被领完'], 400);210        // 防重复领取(同一券每人限领 1 张)211        $chk = $this->db->prepare("SELECT COUNT(*) FROM coupon_user WHERE member_id=? AND coupon_id=?");212        $chk->execute([$member_id, $coupon_id]);213        if ($chk->fetchColumn() > 0) $this->json(['ok' => 0, 'msg' => '您已领取过该券'], 400);214        (new CouponUserModel())->create(['coupon_id' => $coupon_id, 'member_id' => $member_id, 'status' => 'unused']);215        $this->db->prepare("UPDATE coupons SET received=received+1 WHERE id=?")->execute([$coupon_id]);216        $this->json(['ok' => 1, 'msg' => '领取成功']);217    }218219    public function coupon_my() {220        $member_id = (int)$this->input('member_id', 0);221        $st = $this->db->prepare("SELECT cu.*, c.name, c.type, c.threshold, c.value, c.discount, c.valid_days222            FROM coupon_user cu JOIN coupons c ON c.id=cu.coupon_id WHERE cu.member_id=? ORDER BY cu.id DESC");223        $st->execute([$member_id]);224        $data = array_map(function ($r) {225            $desc = $r['type'] === 'discount' ? ($r['discount'] * 10) . '折'226                : ($r['type'] === 'no_threshold' ? '无门槛¥' . $r['value']227                : ($r['threshold'] > 0 ? '满' . $r['threshold'] . '减' . $r['value'] : '¥' . $r['value'] . '券'));228            return ['id' => $r['id'], 'coupon_id' => $r['coupon_id'], 'name' => $r['name'],229                'type' => $r['type'], 'threshold' => (float)$r['threshold'], 'value' => (float)$r['value'],230                'discount' => (float)$r['discount'], 'status' => $r['status'], 'desc' => $desc];231        }, $st->fetchAll());232        $this->json(['ok' => 1, 'data' => $data]);233    }234235    // ---- 订单 ----236    public function order_create() {237        $member_id = (int)$this->input('member_id', 0);238        $items = $this->input('items', []);239        if (!is_array($items) || !$items) $this->json(['ok' => 0, 'msg' => '购物车为空'], 400);240        $contact_name = trim((string)$this->input('contact_name', ''));241        $phone = trim((string)$this->input('phone', ''));242        if (!$contact_name || !$phone) $this->json(['ok' => 0, 'msg' => '请填写收货人信息'], 400);243        $delivery_type = $this->input('delivery_type', 'delivery');244        $delivery_time = trim((string)$this->input('delivery_time', ''));245        $card_msg = trim((string)$this->input('card_msg', ''));246        $remark = trim((string)$this->input('remark', ''));247        $coupon_user_id = (int)$this->input('coupon_user_id', 0);248        $points_used = max(0, (int)$this->input('points_used', 0));249250        $set = [];251        $rs = $this->db->query("SELECT k,v FROM settings");252        foreach ($rs as $row) $set[$row['k']] = $row['v'];253        $fee_cfg = (float)($set['delivery_fee'] ?? 15);254        $free_cfg = (float)($set['free_delivery_amount'] ?? 199);255256        $total = 0; $orderItems = [];257        foreach ($items as $it) {258            $pid = (int)($it['product_id'] ?? 0);259            $sid = (int)($it['sku_id'] ?? 0);260            $qty = max(1, (int)($it['qty'] ?? 1));261            $sku = (new SkuModel())->find($sid);262            $prod = (new ProductModel())->find($pid);263            if (!$sku || !$prod) continue;264            $sub = round((float)$sku['price'] * $qty, 2);265            $total += $sub;266            $orderItems[] = [267                'product_id' => $pid, 'sku_id' => $sid, 'name' => $prod['name'],268                'size' => $sku['size'], 'flavor' => $sku['flavor'],269                'price' => (float)$sku['price'], 'qty' => $qty, 'subtotal' => $sub270            ];271        }272        if (!$orderItems) $this->json(['ok' => 0, 'msg' => '下单失败:商品无效'], 400);273274        // 配送费275        $delivery_fee = 0;276        if ($delivery_type === 'delivery' && $total < $free_cfg) $delivery_fee = $fee_cfg;277278        // 优惠券279        $coupon_amount = 0;280        if ($coupon_user_id) {281            $cu = $this->db->prepare("SELECT cu.*, c.threshold, c.value, c.discount, c.type FROM coupon_user cu JOIN coupons c ON c.id=cu.coupon_id WHERE cu.id=? AND cu.member_id=? AND cu.status='unused'");282            $cu->execute([$coupon_user_id, $member_id]);283            $coupon = $cu->fetch();284            if ($coupon) {285                if ($coupon['type'] === 'discount') {286                    $coupon_amount = round($total * (1 - (float)$coupon['discount']), 2);287                } elseif ($coupon['type'] === 'no_threshold') {288                    $coupon_amount = (float)$coupon['value'];289                } elseif ((float)$coupon['threshold'] <= $total) {290                    $coupon_amount = (float)$coupon['value'];291                }292            }293        }294        // 积分抵扣(100 积分 = 1 元,最多抵 10% 订单额)295        $points_amount = 0;296        if ($points_used > 0) {297            $m = (new MemberModel())->find($member_id);298            $points_used = min($points_used, (int)$m['points']);299            $points_amount = round($points_used / 100, 2);300            $maxPoints = floor($total * 0.1 * 100);301            if ($points_amount > $total * 0.1) { $points_used = $maxPoints; $points_amount = round($maxPoints / 100, 2); }302        }303304        $pay_amount = round(max(0, $total + $delivery_fee - $coupon_amount - $points_amount), 2);305        $order_no = date('YmdHis') . str_pad($member_id, 3, '0', STR_PAD_LEFT) . rand(10, 99);306307        $oid = (new OrderModel())->create([308            'order_no' => $order_no, 'member_id' => $member_id, 'contact_name' => $contact_name,309            'phone' => $phone, 'address' => trim((string)$this->input('address', '')),310            'delivery_type' => $delivery_type, 'delivery_time' => $delivery_time,311            'card_msg' => $card_msg, 'remark' => $remark, 'coupon_id' => $coupon_user_id,312            'coupon_amount' => $coupon_amount, 'points_used' => $points_used,313            'total_amount' => round($total + $delivery_fee, 2), 'pay_amount' => $pay_amount,314            'status' => 'paid'315        ]);316        foreach ($orderItems as $oi) {317            (new OrderItemModel())->create(array_merge($oi, ['order_id' => $oid]));318            $this->db->prepare("UPDATE skus SET stock=stock-?, sales=sales+? WHERE id=?")319                ->execute([$oi['qty'], $oi['qty'], $oi['sku_id']]);320        }321        // 清空购物车(用户维度)322        $this->db->prepare("DELETE FROM carts WHERE member_id=?")->execute([$member_id]);323        // 优惠券标记已用324        if ($coupon_user_id) $this->db->prepare("UPDATE coupon_user SET status='used', used_at=datetime('now','localtime') WHERE id=?")->execute([$coupon_user_id]);325        // 积分:消费得积分(1 元 = 1 分)+ 成长值326        if ($member_id) {327            $earn = (int)$pay_amount;328            $this->db->prepare("UPDATE members SET points=points+?, growth=growth+? WHERE id=?")329                ->execute([$earn, $earn, $member_id]);330        }331        $this->json(['ok' => 1, 'order_id' => $oid, 'order_no' => $order_no, 'pay_amount' => $pay_amount]);332    }333334    public function order_list() {335        $member_id = (int)$this->input('member_id', 0);336        $status = trim((string)$this->input('status', ''));337        $sql = "SELECT * FROM orders WHERE member_id=?";338        $params = [$member_id];339        if ($status) { $sql .= " AND status=?"; $params[] = $status; }340        $sql .= " ORDER BY id DESC";341        $st = $this->db->prepare($sql);342        $st->execute($params);343        $data = [];344        foreach ($st->fetchAll() as $o) {345            $it = $this->db->prepare("SELECT * FROM order_items WHERE order_id=?");346            $it->execute([$o['id']]);347            $data[] = array_merge($o, ['items' => $it->fetchAll()]);348        }349        $this->json(['ok' => 1, 'data' => $data]);350    }351352    public function order_detail() {353        $id = (int)$this->input('id', 0);354        $o = (new OrderModel())->find($id);355        if (!$o) $this->json(['ok' => 0, 'msg' => '订单不存在'], 404);356        $it = $this->db->prepare("SELECT * FROM order_items WHERE order_id=?");357        $it->execute([$id]);358        $this->json(['ok' => 1, 'order' => array_merge($o, ['items' => $it->fetchAll()])]);359    }360361    public function order_cancel() {362        $id = (int)$this->input('id', 0);363        $o = (new OrderModel())->find($id);364        if (!$o) $this->json(['ok' => 0, 'msg' => '订单不存在'], 404);365        if (!in_array($o['status'], ['pending', 'paid', 'making'])) {366            $this->json(['ok' => 0, 'msg' => '当前状态不可取消'], 400);367        }368        (new OrderModel())->update($id, ['status' => 'canceled']);369        $this->json(['ok' => 1, 'msg' => '已取消']);370    }371372    public function order_review() {373        $order_id = (int)$this->input('order_id', 0);374        $product_id = (int)$this->input('product_id', 0);375        $member_id = (int)$this->input('member_id', 0);376        $rating = max(1, min(5, (int)$this->input('rating', 5)));377        $content = trim((string)$this->input('content', ''));378        (new ReviewModel())->create([379            'order_id' => $order_id, 'product_id' => $product_id, 'member_id' => $member_id,380            'rating' => $rating, 'content' => $content, 'images' => '[]', 'status' => 1381        ]);382        (new OrderModel())->update($order_id, ['status' => 'done']);383        $this->json(['ok' => 1, 'msg' => '评价成功']);384    }385386    // ---- 定制蛋糕 ----387    public function custom_submit() {388        $member_id = (int)$this->input('member_id', 0);389        $image = trim((string)$this->input('image', ''));390        $text = trim((string)$this->input('text', ''));391        $shape = trim((string)$this->input('shape', ''));392        $size = trim((string)$this->input('size', '8寸'));393        $note = trim((string)$this->input('note', ''));394        (new CustomCakeModel())->create([395            'member_id' => $member_id, 'image' => $image, 'text' => $text,396            'shape' => $shape, 'size' => $size, 'note' => $note, 'status' => 'pending'397        ]);398        $this->json(['ok' => 1, 'msg' => '定制需求已提交,客服将尽快与您确认']);399    }400401    // ---- 店铺设置 ----402    public function settings() {403        $rs = $this->db->query("SELECT k,v FROM settings");404        $data = [];405        foreach ($rs as $row) $data[$row['k']] = $row['v'];406        $this->json(['ok' => 1, 'data' => $data]);407    }408}409