文件
MiAn cb12bf5fc8 feat: 新增地址选择插件及订阅消息等多项功能优化
- 新增地址选择插件,支持地图位置选择并返回地址和经纬度
- 新增微信订阅消息发送功能,支持审核结果、提现通知等场景
- 新增订单支付状态查询、提现收款人姓名保存等接口
- 新增服务区域管理,支持按省市区划分并同步到信息发布
- 新增资金流水和营收报表导出功能
- 新增可用优惠券查询、订单退款拆分处理等功能
- 优化后台角标显示、首页数据统计、UI配置下发等
- 修复订单支付并发问题、地区管理逻辑等
2026-10-22 17:14:56 +08:00

196 行
6.8 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\api\controller;
use think\Db;
/**
* 用户端 · 优惠券(Module-COUPON)
* 路由:
* GET /api/v1/coupon/list 可领取优惠券分页列表(带 received 已领取标记)
* POST /api/v1/coupon/receive 领取优惠券
*
* 数据表:fa_fc_coupon(券模板)/ fa_fc_user_coupon(领取记录)。
* 说明:本轮仅做「领取 + 我的资产展示」,**不含下单抵扣**(下单抵扣在后续版本接入)。
*/
class Coupon extends V1
{
/**
* 优惠券功能总开关(后台「系统设置」写入 fc_config 的 coupon.enabled,默认开启)
* 关闭后用户端列表/领取一律拒绝,避免「后台关了前端还能领」。
*/
protected function assertEnabled()
{
if (!(int)$this->cfg('coupon.enabled', 1)) {
$this->fail('优惠券功能暂未开放');
}
}
/**
* GET /api/v1/coupon/list {page,size}
* → 分页 {list,page,size,total,hasMore}
* 列表项:{id,couponId,name,type,amount,minAmount,total,receivedCount,left,days,received,createdAt}
* 规则:仅 enabled=1(停用券不下发);received 表示「当前用户是否已领取」;
* left = 剩余可领数量(-1 表示不限量);total=0 表示不限量。
*/
public function list()
{
$this->assertEnabled();
$user = $this->auth();
list($rows, $total, $page, $size) = $this->pageQuery(function ($forCount) {
$q = Db::name('fc_coupon')->where('enabled', 1);
if ($forCount) {
return $q;
}
return $q->order('sort desc, id desc');
});
$list = [];
foreach ($rows as $row) {
$received = (bool)Db::name('fc_user_coupon')
->where('user_id', $user['id'])
->where('coupon_id', $row['id'])
->find();
$list[] = $this->couponItem($row, $received);
}
$this->ok($this->paginate($list, $total, $page, $size));
}
/**
* POST /api/v1/coupon/receive {couponId}
* → {userCouponId}
* 规则:券必须存在且 enabled=1;领完 → fail('优惠券已领完');
* 同一用户同一张券只能领一次 → fail('您已领取过该优惠券');
* days>0 时按「领取时间 + days 天」写过期时间,days=0 表示永久有效。
*/
public function receive()
{
$this->assertEnabled();
$user = $this->auth();
$couponId = (int)$this->input('couponId', 0);
$coupon = $couponId > 0 ? Db::name('fc_coupon')->where('id', $couponId)->find() : null;
if (!$coupon || (int)$coupon['enabled'] !== 1) {
$this->fail('优惠券不存在');
}
$total = (int)$coupon['total'];
$receivedCount = (int)$coupon['received'];
if ($total > 0 && $receivedCount >= $total) {
$this->fail('优惠券已领完');
}
$exist = Db::name('fc_user_coupon')
->where('user_id', $user['id'])
->where('coupon_id', $couponId)
->find();
if ($exist) {
$this->fail('您已领取过该优惠券');
}
$now = time();
$days = max(0, (int)$coupon['days']);
try {
$userCouponId = Db::name('fc_user_coupon')->insertGetId([
'user_id' => $user['id'],
'coupon_id' => $couponId,
'status' => 'unused',
'order_no' => '',
'usetime' => null,
'expiretime' => $days > 0 ? $now + $days * 86400 : null,
'createtime' => $now,
]);
} catch (\Exception $e) {
// 并发重复领取兜底
$this->fail('您已领取过该优惠券');
}
// 已领取数 +1(setInc 自增,避免并发覆盖)
Db::name('fc_coupon')->where('id', $couponId)->setInc('received', 1);
$this->ok(['userCouponId' => $userCouponId]);
}
/**
* GET /api/v1/coupon/usable?amount=xx
* 下单可用券列表(按金额计算可抵扣金额)
*/
public function usable()
{
$this->assertEnabled();
$user = $this->auth();
$amount = round((float)$this->input('amount', 0), 2);
if ($amount <= 0) {
$this->ok([]);
}
$now = time();
$rows = Db::name('fc_user_coupon')->alias('uc')
->join('fc_coupon c', 'c.id = uc.coupon_id')
->where('uc.user_id', $user['id'])
->where('uc.status', 'unused')
->where('c.enabled', 1)
->where('c.min_amount', '<=', $amount)
->where(function ($q) use ($now) {
$q->whereNull('uc.expiretime')->whereOr('uc.expiretime', '>', $now);
})
->field('uc.id,uc.coupon_id,uc.expiretime,c.name,c.type,c.amount,c.min_amount')
->order('c.sort desc, c.id desc')
->select();
$list = [];
foreach ($rows as $row) {
try {
$p = \app\common\library\CouponService::preview($user['id'], (int)$row['id'], $amount);
} catch (\Throwable $e) {
continue;
}
$list[] = [
'userCouponId' => (int)$row['id'],
'couponId' => (int)$row['coupon_id'],
'name' => (string)$row['name'],
'type' => (string)$row['type'],
'amount' => (float)$row['amount'],
'minAmount' => (float)$row['min_amount'],
'discount' => $p['discount'],
'payable' => $p['payable'],
'expireAt' => !empty($row['expiretime']) ? $this->iso($row['expiretime']) : null,
];
}
$this->ok($list);
}
/**
* 券模板 → 列表项
* @param array $row fc_coupon 行
* @param bool $received 当前用户是否已领取
* @return array
*/
protected function couponItem($row, $received)
{
$total = (int)$row['total'];
$receivedCount = (int)$row['received'];
return [
'id' => (int)$row['id'],
'couponId' => (int)$row['id'],
'name' => (string)$row['name'],
'type' => (string)$row['type'],
'amount' => (float)$row['amount'],
'minAmount' => (float)$row['min_amount'],
'total' => $total,
'receivedCount' => $receivedCount,
// -1 表示不限量(total=0)
'left' => $total > 0 ? max(0, $total - $receivedCount) : -1,
'days' => (int)$row['days'],
'received' => (bool)$received,
'createdAt' => $this->iso($row['createtime']),
];
}
}