这里的“扣取金币”通常指的是用户在网站上进行某些操作(如下载资源、查看付费内容、发布帖子等)时,系统自动扣除其账户内的金币,这个功能的核心在于验证用户金币是否足够和执行扣除操作。

(图片来源网络,侵删)
下面我将提供一个完整、可直接使用的代码方案,并附上详细的步骤说明和代码解析。
我们将创建一个自定义的PHP函数,这个函数可以安全地处理金币的扣除逻辑,在需要扣除金币的地方(比如下载页面的逻辑中)调用这个函数即可。
这个方案的核心是利用DedeCMS自带的 member 数据库表,该表存储了会员信息,scores 字段就是我们通常所说的“金币”。
第一步:创建核心的扣除金币函数
这是整个功能的核心,我们把它放在一个公共函数文件中,方便全局调用。

(图片来源网络,侵删)
-
找到并打开
include/helpers/extend.helper.php文件。 这个文件是专门用来存放自定义函数的,织梦系统会自动加载它。 -
在文件末尾
?>之前,添加以下代码:
/**
* 扣除用户金币
*
* @param int $mid 用户ID
* @param int $score 扣除的金币数量
* @param string $note 扣除原因备注
* @param bool $isCheck 是否只检查而不扣除
* @return array 返回操作结果状态和信息
* ['status' => true/false, 'msg' => '成功/失败原因']
*/
function DedecostScores($mid, $score, $note = '', $isCheck = false)
{
// 参数校验
if (empty($mid) || $mid <= 0 || empty($score) || $score <= 0) {
return ['status' => false, 'msg' => '参数错误,用户ID或金币数量无效'];
}
// 连接数据库
global $dsql;
// 查询用户当前金币
$row = $dsql->GetOne("SELECT scores FROM `#@__member` WHERE mid = '{$mid}'");
// 检查用户是否存在
if (!$row) {
return ['status' => false, 'msg' => '用户不存在'];
}
// 检查金币是否足够
if ($row['scores'] < $score) {
return ['status' => false, 'msg' => '金币不足,当前金币:' . $row['scores']];
}
// 如果只是检查,不执行扣除
if ($isCheck) {
return ['status' => true, 'msg' => '检查通过,金币充足'];
}
// 开始事务处理,确保数据一致性
$dsql->ExecuteNoneQuery("START TRANSACTION");
try {
// 执行扣除金币
$sql = "UPDATE `#@__member` SET scores = scores - {$score} WHERE mid = '{$mid}' AND scores >= {$score}";
$dsql->ExecuteNoneQuery($sql);
// 检查是否真的更新了数据(防止并发问题)
$affectedRows = $dsql->AffectedRows();
if ($affectedRows <= 0) {
throw new Exception('金币扣除失败,可能被其他操作抢先扣除。');
}
// 记录金币变动日志(强烈推荐)
$logData = array(
'mid' => $mid,
'score' => -$score, // 扣除为负数
'admin' => 'system', // 可以是操作员ID或用户名
'sendtime' => time(),
'note' => $note,
);
$fields = array();
$values = array();
foreach ($logData as $key => $value) {
$fields[] = "`{$key}`";
$values[] = "'" . addslashes($value) . "'";
}
$sql_log = "INSERT INTO `#@__member_scores_log` (" . implode(',', $fields) . ") VALUES (" . implode(',', $values) . ")";
$dsql->ExecuteNoneQuery($sql_log);
// 提交事务
$dsql->ExecuteNoneQuery("COMMIT");
return ['status' => true, 'msg' => '金币扣除成功'];
} catch (Exception $e) {
// 发生异常,回滚事务
$dsql->ExecuteNoneQuery("ROLLBACK");
return ['status' => false, 'msg' => '金币扣除失败:' . $e->getMessage()];
}
}
代码解析:
- 参数:
$mid: 用户的ID。$score: 要扣除的金币数量。$note: 扣除原因,方便后台查看日志。$isCheck: 一个非常有用的参数,设为true时,函数只检查金币是否足够,并不真正扣除,可以用于下单前的预检查。
- 事务处理 (
START TRANSACTION,COMMIT,ROLLBACK):这是保证数据安全的关键,它确保“查询用户金币”和“更新用户金币”这两个操作要么全部成功,要么全部失败,避免在并发请求时出现扣款错误。
- 日志记录 (
#@__member_scores_log):- 强烈建议你在数据库中创建一个名为
dede_member_scores_log的表来记录所有金币变动,这样你就可以清楚地知道谁在什么时候因为什么原因扣除了多少金币。 - 建议的表结构:
CREATE TABLE `dede_member_scores_log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `mid` int(11) NOT NULL COMMENT '用户ID', `score` int(11) NOT NULL COMMENT '变动分数,扣除为负数', `admin` varchar(50) NOT NULL COMMENT '操作员', `sendtime` int(11) NOT NULL COMMENT '操作时间', `note` varchar(255) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
- 强烈建议你在数据库中创建一个名为
- 返回值:
- 函数返回一个数组,包含
status(布尔值,表示成功或失败) 和msg(字符串,描述具体信息),这样调用方可以根据返回值进行相应的处理(如提示用户)。
- 函数返回一个数组,包含
第二步:在需要的地方调用该函数
你可以在任何需要扣除金币的PHP文件中调用这个函数,最常见的场景是资源下载页面。
假设你的下载页面是 plus/download.php,你希望用户下载一个需要付费的资源时扣除10个金币。
- 打开你的下载处理文件(
plus/download.php或你自定义的下载模板文件)。 - 找到处理下载逻辑的核心部分,在执行下载操作之前,加入金币扣除的代码。
示例代码 (在 plus/download.php 中修改):
<?php
require_once(dirname(__FILE__)."/../include/common.inc.php");
require_once(DEDEINC."/dedetemplate.class.php");
// 假设你已经获取了当前登录用户的信息
$cfg_ml = new MemberLogin(); // 实例化会员登录类
if ($cfg_ml->IsLogin()) {
$mid = $cfg_ml->M_ID; // 获取当前登录用户的ID
$userName = $cfg_ml->M_UserName;
} else {
ShowMsg('请先登录!', '-1');
exit();
}
// --- 以下是你的下载逻辑 ---
// 假设 $arcid 是你要下载的文档ID
$arcid = isset($arcid) ? intval($arcid) : 0;
// 1. 检查该资源是否需要付费
// 这里你需要根据你的业务逻辑来判断,例如查询附加表字段
$row = $dsql->GetOne("SELECT isscores, scores FROM `#@__addonarticle` WHERE aid = '{$arcid}'");
if (!$row) {
ShowMsg('资源不存在!', '-1');
exit();
}
if ($row['isscores'] == 1) { // 假设 isscores=1 表示需要付费
$costScore = isset($row['scores']) ? intval($row['scores']) : 10; // 获取需要的金币数,默认10
// 2. 调用我们的函数扣除金币
$result = DedecostScores($mid, $costScore, "下载资源:ID {$arcid}");
// 3. 根据返回结果处理
if ($result['status'] === false) {
// 扣除失败,提示用户
ShowMsg($result['msg'], '-1');
exit();
} else {
// 扣除成功,继续执行下载逻辑
// ... 你的下载代码,
// $link = GetDownloadLink($arcid);
// header("Location: $link");
echo "扣除 {$costScore} 金币成功!正在为您准备下载...";
}
} else {
// 不需要付费,直接下载
echo "免费资源,直接下载...";
// ... 你的下载代码
}
?>
调用流程解析:
- 首先检查用户是否登录。
- 获取要下载的资源的ID (
$arcid)。 - 查询数据库,判断这个资源是否需要付费(通过
#@__addonarticle表的isscores字段)。 - 如果需要付费,调用我们之前创建的
DedecostScores()函数。 - 检查函数返回值:
- 如果返回
status为false,说明操作失败(比如金币不足),用ShowMsg()提示用户并终止程序。 - 如果返回
status为true,说明扣除成功,继续执行后续的下载操作(如文件下载、跳转等)。
- 如果返回
第三步:后台管理(可选但推荐)
为了方便管理员管理用户金币,你可以在后台增加一个手动扣分/加分的入口。
- 找到后台会员管理文件,如
dede/member_group_edit.php或dede/member_edit.php。 - 在页面上添加一个表单,输入用户ID和分数,并调用我们的函数。
- 或者,直接在数据库中手动修改
#@__member表的scores字段,并手动向#@__member_scores_log表添加一条记录。
通过以上三步,你就为你的DedeCMS网站实现了一个健壮、安全的金币扣除功能。
核心要点回顾:
- 核心函数: 将扣除逻辑封装在
extend.helper.php的DedecostScores()函数中。 - 安全机制: 使用数据库事务确保数据一致性,防止并发问题。
- 日志追踪: 创建金币变动日志表,所有操作都有据可查。
- 灵活调用: 在业务逻辑中(如下载页面)调用该函数,并根据返回值进行判断和提示。
- 参数校验: 函数内部和调用处都应进行必要的参数校验。
这个方案比直接在模板里写SQL语句要安全、规范得多,也更容易维护和扩展。
