Discuz 在线业务办理插件开发模板¶
1. 需求分析与规划¶
1.1 用户角色定义¶
本房屋保险业务功能模块涉及以下三类用户角色,每类用户拥有不同操作权限:
- 普通用户(投保人):通过微信小程序提交房屋保险申请订单,查看订单状态,接收报价信息,确认订单并完成支付,最终查看保险单据。
- 商户用户(客服人员):通过商户管理中心(xigua_partnercenter 插件)登录管理后台,查看订单列表,填写报价信息,上传保险单据等。
- 系统管理员:具备全局数据管理权限,包括商户账号授权、配置邮件/模板消息通知、管理插件配置和数据导出等功能。
系统要求普通用户登录微信小程序后方可提交订单;商户用户通过绑定 UID 方式获得访问权限,仅能管理自己业务类型下的订单数据。
1.2 功能边界与数据安全¶
结合 Discuz! 插件机制与小程序前端应用场景,本模块采用以下边界和安全策略:
- 数据隔离机制:所有订单记录依据“业务类型(biz_type)+UID”进行隔离,商户用户不可跨业务访问他人数据。
- 订单状态管控:设置完整状态流转(草稿 → 报价待确认 → 待支付 → 待上传单据 → 完成)控制流程权限与页面显示内容。
- 文件安全控制:保险单据等附件仅支持后台上传,存储于受限目录,文件名使用加密命名策略,防止未授权访问。
- 小程序跳转与链接约束:所有用户操作集中在小程序内实现,不通过邮件或外部跳转链接进行操作,防止用户丢失登录态。
- 权限验证与审计:每次报价、文件上传、状态变更操作均记录 UID、操作时间与来源 IP,方便系统追踪与审核。
2. 功能设计¶
2.1 插件使用流程说明(用户视角)¶
- 使用流程概述
本插件为“购买房屋保险”业务提供完整在线办理闭环,服务部署于58海外平台站点,但用户主要通过微信小程序端访问和操作。所有交互流程在小程序内完成,包括提交订单、接收报价、确认办理、完成支付及查看保险资料等。
- 流程步骤如下:
1) 用户访问分类信息首页(微信小程序或移动站点)plugin.php?id=xigua_hb,点击“购买房屋保险”进入插件页面:plugin.php?id=xigua_houseinsurance。
2) 在页面中选择房屋类型、填写投保信息(如房产信息、联系方式、邮箱等),勾选服务协议后点击“提交订单”。
3) 系统生成订单记录,并通过邮箱将订单摘要通知发送至商户客服邮箱(仅作提示用)。
4) 商户客服人员收到通知后,登录 plugin.php?id=xigua_partnercenter&biz=xigua_houseinsurance 商户管理后台,查看并核算订单报价。
5) 客服人员填写报价金额和说明后提交,系统更新订单状态为“待客户确认报价”。
6) 客户在小程序中访问“我的订单”页面(如:plugin.php?id=xigua_houseinsurance&mod=myorders),点击进入对应订单,查看报价详情。
7) 用户点击“同意报价并办理”按钮确认报价,订单状态更新为“待支付”。
8) 用户通过小程序提供的支付页面完成支付(可跳转至收款码或生成凭证上传)。
9) 系统记录支付成功信息,并通过邮件/系统消息通知商户客服,订单状态更新为“待上传单据”。
10) 商户登录后台,上传保险单据文件(PDF/图片等),系统保存并记录上传操作。
11) 用户再次在小程序中查看订单详情,可在线预览或下载保险单据。
12) 订单状态更新为“已完成”,用户和商户均可在后台查看完整流程记录。
2.2 功能模块分解(技术视角)¶
2.2.1 前端模块¶
本模块前端页面基于 Discuz! 插件模板机制开发,适配微信小程序 WebView 访问,重点关注响应式布局、交互便捷与视图一致性,主要包括以下部分:
- 房屋保险申请表单页面
- 提供房屋类型、地址、面积、用户信息等输入字段;
- 服务协议与隐私政策勾选项,提交按钮防止重复提交;
- 表单页面模板位于 template/touch/front_order_form.htm。
- 提交成功提示页
- 显示“订单提交成功”信息;
- 提示后续流程(等待商户报价);
- 页面模板参考 template/touch/success_dialog.htm。
- 我的订单页(订单列表)
- 展示当前 UID 所有房屋保险订单;
- 显示状态、报价信息、操作按钮(确认、支付、查看);
- 可通过参数调用模板复用 order_list.htm。
- 订单详情页
- 展示订单详细内容、报价内容、保险单据预览;
- 状态变更引导用户完成支付或查看结果。
- 移动端优化
- 所有页面基于 flex 或 百分比布局;
- 兼容微信内嵌浏览器和 Safari/Android 浏览器渲染;
- 图标采用 Unicode 字符或 base64 内嵌,避免字体加载失败问题。
2.2.2 后端服务处理¶
后端控制器采用插件主控制器(.inc.php)+ 子控制器结构,集中处理数据提交、状态管理与业务逻辑封装,关键模块如下:
- 表单提交处理
- 提交表单通过 POST 请求发送至 xigua_houseinsurance.inc.php;
- 服务端进行字段合法性校验、防重复提交控制(基于 formhash 和 IP+时间戳);
- 提交成功后写入订单表(如:pre_xigua_houseinsurance_order);
- 同步生成初始订单状态记录并发送商户通知。
- 报价提交与管理
- 商户登录后访问商户中心插件的子控制器 houseinsurance_order.inc.php;
- 支持报价金额、报价说明输入与保存;
- 修改后更新订单状态并记录日志。
- 状态标记与流程控制
- 系统定义多阶段状态字段(如:submitted、quoted、confirmed、paid、completed);
- 每次状态变更需伴随日志记录与通知操作;
- 禁止用户跳过状态顺序直接操作。
- 操作日志机制
- 所有提交、修改、确认、上传等行为写入专用日志表或作为订单扩展记录;
- 每条日志包含 UID、时间、操作类型与相关数据快照。
- 错误处理与用户提示
- 所有后端操作返回统一 JSON 错误码与描述(用于小程序端解析);
- 支持常见错误提示,如表单缺失字段、非法访问、状态异常等;
- 所有接口严格校验用户身份与订单归属。
2.3 后台管理功能(管理员视角)¶
该插件将提供基础后台管理功能模块,供系统管理员配置运行参数、查看操作记录并支持数据导出等功能。
- 插件后台入口
- 安装插件后,在 Discuz 管理后台“插件”菜单中显示“房屋保险在线办理插件(xigua_houseinsurance)”入口;
- 点击后进入插件管理页面,可配置插件运行参数、查看订单统计等。
- 后台配置变量
- 后台设计一个字符串变量:insurance_promotions,用于填写“优惠通知”内容;
- 前台模板页面将提供“优惠通知”按钮,点击后跳转展示该变量内容,支持 HTML 格式(如服务说明、图片、联系方式等);
- 该功能适用于展示最新保险促销政策、报价规则调整、产品介绍等场景,增强用户转化率。
- 操作记录查看
- 插件支持查看操作日志(包括订单创建、报价变更、状态流转、上传单据等);
- 日志记录包括时间、操作者 UID、操作内容与关键字段变化。
- 数据导出功能(可选)
- 支持按时间范围导出订单数据为 CSV 格式;
- 字段包括订单号、客户信息、房屋信息、状态、报价金额、支付状态等;
- 导出功能仅限系统管理员使用,确保信息安全。
2.4 后续可扩展方向(预留)¶
该插件框架采用模块化设计,具备良好扩展性,未来可按以下方向增强系统功能:
- 支持更多业务类型
- 当前插件以 biz_type=houseinsurance 为主,未来可基于此结构扩展其他保险类型(如车险、旅行险);
- 支持同一用户提交多类保险申请,订单隔离管理。
- 高级权限分级机制
- 商户端可进一步细化权限角色(如报价员、审核员、财务);
- 支持权限分离与操作范围限制,提高安全性与可审计性。
- 订单进度自动化流转
- 当前为半自动流转(用户与商户手动确认状态),后续可引入审批流控逻辑;
- 系统可自动识别是否满足转入下一个阶段的条件(如支付成功后自动切换状态、定时提醒)。
- 站内消息/模板消息集成
- 微信小程序端引入模板消息机制,实现报价提醒、支付成功提示、单据通知等;
- 提高通知效率,增强用户体验。
- 多语言支持
- 增加语言包配置,支持海外用户群体切换英文、法文等界面语言。
3. 插件开发¶
3.1 文件结构规划¶
source/plugin/
├── xigua_partnercenter/ ← 商户管理中心插件(多业务管理平台)
│ ├── admin_account.inc.php # 后台管理子账号(已存在)
│ ├── table/
│ │ └── table_xigua_partnercenter_account.php # 商户账号数据表操作类
│ ├── template/
│ │ ├── front/(暂未用到)
│ │ ├── static/(暂未用到)
│ │ └── touch/(暂未用到)
│ ├── plugin.xml # 注册插件和后台菜单(待扩展为配置业务插件列表)
│ ├── install.php / uninstall.php # 安装/卸载逻辑(如初始化商户账户表)
│ ├── xigua_partnercenter.inc.php # 插件入口,按 biz=xxx 分发业务处理
│ └── xigua_partnercenter.lang.php # 插件语言包
├── xigua_houseinsurance/
│ ├── file/ # ⬅️ 用户上传文件与客服资源(如保单、二维码等)
│ │ └── *.pdf / *.png # 保单文件、客服二维码图等
│ │
│ ├── inc/ # 控制器逻辑(商户端处理)
│ │ └── xigua_houseinsurance_order.inc.php
│ │
│ ├── table/ # 数据表操作封装类
│ │ └── table_xigua_houseinsurance_order.php
│ │
│ ├── template/ # 模板资源
│ │ ├── front/ # PC端模板(如后台报表)
│ │ │ └── order_list.htm
│ │
│ │ ├── static/ # 静态资源(css/icon等)
│ │ │ ├── custom.css
│ │ │ └── iconfont.woff2
│ │
│ │ └── touch/ # 移动端模板(用户/商户/公共)
│ │ ├── user_order_form.htm # ✅ 用户提交订单页面
│ │ ├── manage_order_list.htm # ✅ 商户查看订单列表
│ │ ├── manage_order_detail.htm # ✅ 商户处理订单详情
│ │ ├── dialog_noupdate.htm # 弹窗:无需更新提示
│ │ ├── dialog_privacy.htm # 弹窗:隐私协议
│ │ ├── dialog_promotion_notice.htm # 弹窗:活动提示
│ │ ├── dialog_success_update.htm # 弹窗:提交成功提示
│ │ ├── dialog_terms.htm # 弹窗:服务条款
│ │ ├── common_header.php # 公共页头
│ │ └── common_footer.php # 公共页脚(建议补全)
│ │
│ ├── plugin.xml # 插件定义文件
│ ├── install.php / uninstall.php # 安装与卸载脚本
│ ├── xigua_houseinsurance.inc.php # 插件入口(用户默认页)
│ └── xigua_houseinsurance.lang.php # 插件语言包
3.2 数据表设计¶
主业务数据表:hwxigua_houseinsurance_order
用于记录每一笔房屋保险申请订单的核心数据。
CREATE TABLE IF NOT EXISTS `hwxigua_houseinsurance_order` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`uid` INT(10) UNSIGNED NOT NULL COMMENT '提交用户UID',
`house_type` VARCHAR(50) NOT NULL COMMENT '房屋类型',
`house_area` VARCHAR(30) DEFAULT '' COMMENT '面积信息',
`address` VARCHAR(255) DEFAULT '' COMMENT '房产地址',
`contact_name` VARCHAR(50) DEFAULT '' COMMENT '联系人姓名',
`contact_email` VARCHAR(100) DEFAULT '' COMMENT '邮箱',
`contact_phone` VARCHAR(30) DEFAULT '' COMMENT '联系电话',
`extra_info` TEXT COMMENT '用户备注',
`status` TINYINT DEFAULT 0 COMMENT '订单状态:0待处理,1已报价,2已付款,3已完成',
`quote_price` DECIMAL(10,2) DEFAULT NULL COMMENT '商户填写报价金额',
`quote_remark` TEXT COMMENT '报价说明',
`policy_file` VARCHAR(255) DEFAULT '' COMMENT '上传的保单文件路径',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
设计要点说明:
- 业务数据表前缀统一为 hw 开头,表名应为 hwxigua_houseinsurance_order;
- 订单号字段为系统自动生成的唯一编号,生成规则如下:
新表结构定义:
CREATE TABLE IF NOT EXISTS `hwxigua_houseinsurance_order` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`order_no` VARCHAR(50) NOT NULL COMMENT '订单编号(唯一)',
`uid` INT(10) UNSIGNED NOT NULL COMMENT '提交用户UID',
`house_type` VARCHAR(50) NOT NULL COMMENT '房屋类型',
`house_area` VARCHAR(30) DEFAULT '' COMMENT '面积信息',
`address` VARCHAR(255) DEFAULT '' COMMENT '房产地址',
`contact_name` VARCHAR(50) DEFAULT '' COMMENT '联系人姓名',
`contact_email` VARCHAR(100) DEFAULT '' COMMENT '邮箱',
`contact_phone` VARCHAR(30) DEFAULT '' COMMENT '联系电话',
`extra_info` TEXT COMMENT '用户备注',
`status` TINYINT DEFAULT 0 COMMENT '订单状态:0待处理,1已报价,2用户确认,3已付款,4已完成',
`quote_price` DECIMAL(10,2) DEFAULT NULL COMMENT '商户填写报价金额',
`quote_remark` TEXT COMMENT '报价说明',
`policy_file` VARCHAR(255) DEFAULT '' COMMENT '上传的保单文件路径',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_order_no` (`order_no`),
KEY `idx_uid` (`uid`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
设计要点说明:
- 所有订单按 UID 区分归属,按状态字段驱动流程。
- 所有报价/说明/保单字段均在单表内完成记录,便于查询与展示。
- policy_file 字段仅记录相对路径,文件实际存储于受控目录下。
- 支持状态过滤、用户列表分页、商户侧筛选等性能优化。
3.3 plugin.xml 配置¶
<identifier>、<modules>、<menu>配置<setting>支持后台参数项
<?xml version="1.0" encoding="UTF-8"?>
<root>
<plugin>
<identifier>xigua_houseinsurance</identifier>
<name>房屋保险在线办理</name>
<version>1.0</version>
<description><![CDATA[用户可在线申请房屋保险,商户进行报价,确认后完成支付与投保流程。支持订单进度追踪与文档上传。]]></description>
<copyright><![CDATA[© 西蒙2.0]]></copyright>
<installfile>install.php</installfile>
<uninstallfile>uninstall.php</uninstallfile>
<langfile>xigua_houseinsurance.lang.php</langfile>
<settingfile></settingfile>
</plugin>
<!-- 前台模块 -->
<modules>
<module>
<menu>提交申请</menu>
<name>房屋保险申请表单</name>
<url>plugin.php?id=xigua_houseinsurance</url>
</module>
</modules>
<!-- 后台菜单 -->
<menu>
<item id="menu_xigua_houseinsurance_manage">房屋保险插件管理</item>
</menu>
<!-- 后台变量设置 -->
<settings>
<item id="insurance_promotions" type="textarea" rows="8" cols="80">
<title>优惠通知内容</title>
<description>填写将展示在前台“优惠通知”页面的内容,支持 HTML。</description>
<default><![CDATA[<h3>本月房屋保险优惠政策</h3><p>详情请联系客服或点击申请查看。</p>]]></default>
</item>
</settings>
</root>
配置说明:
:用于 Discuz 内唯一标识插件,必须与插件目录名一致; :定义插件前台访问模块入口,用户可从分类信息页跳转访问; :定义后台变量 insurance_promotions,用于展示“优惠通知”内容页面,支持 HTML 富文本格式。
3.4 页面模板开发¶
- 模板文件均置于:
source/plugin/xigua_houseinsurance/template/touch/ - 采用命名规范清晰表达用途:
- front_order_form.htm ← 表单填写页(移动端)
- success_dialog.htm ← 成功提交提示页
- order_list.htm ← 用户订单列表页
- order_detail.htm ← 查看报价 / 支付确认页
- protocol_terms.htm ← 办理协议模板
- protocol_privacy.htm ← 隐私协议模板
- common_header.php ← 通用页头模板
- promotion_notice.htm ← 优惠通知 / 产品说明页
- 模板变量与绑定规范: Discuz 模板系统默认使用 {} 语法表示变量,以下为关键模板变量绑定说明:
- 表单页 front_order_form.htm
<!DOCTYPE html>
<!-- 保留原有多系统 class,可用于响应式样式控制 -->
<html lang="zh-cmn-Hans" class="pixel-ratio-2 retina ios ios-16 ios-16-6 ios-gt-15 ios-gt-14 ios-gt-13 ios-gt-12 ios-gt-11 ios-gt-10 ios-gt-9 ios-gt-8 ios-gt-7 ios-gt-6">
<head>
<meta charset="UTF-8">
<!-- viewport-fit=cover 可配合安全区域适配 iOS 刘海屏 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>58海外</title>
<link rel="stylesheet" href="source/plugin/xigua_houseinsurance/template/static/custom.css">
<!-- 弹出层样式 -->
<style>
#wechatModal {
display: none;
position: fixed;
z-index: 9999;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
}
#wechatModalContent {
background-color: #fefefe;
margin: 20% auto;
padding: 15px;
border-radius: 8px;
width: 80%;
max-width: 280px;
text-align: center;
}
#wechatModalContent img {
width: 100%;
height: auto;
border-radius: 6px;
}
#closeModal {
color: #aaa;
float: right;
font-size: 20px;
font-weight: bold;
}
#closeModal:hover,
#closeModal:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
</style>
</head>
<body>
<!-- 页头模板 -->
<!--{template xigua_houseinsurance:touch/common_header}-->
<div class="page__content" style="padding-top: 2.2rem; padding-bottom: 0.5rem;">
<form method="post" autocomplete="off" action="plugin.php?id=xigua_houseinsurance" class="weui-form">
<input type="hidden" name="formhash" value="{FORMHASH}">
<input type="hidden" name="ordersubmit" value="yes">
<!-- 房屋类型选择 -->
<div class="weui-cell weui-cell_select">
<div class="weui-cell__bd">
<select class="weui-select" name="house_type" required>
<option value="">请选择房屋类型</option>
<option value="apartment">公寓</option>
<option value="villa">独立屋/联排</option>
</select>
</div>
</div>
<!-- 面积输入 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<input class="weui-input" type="number" name="house_area" placeholder="房屋面积(平方米)" required />
</div>
</div>
<!-- 地址输入 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<textarea class="weui-textarea" rows="2" name="address" placeholder="详细地址" required maxlength="100"></textarea>
</div>
</div>
<!-- 姓名 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<input class="weui-input" type="text" name="contact_name" placeholder="姓名" required maxlength="20" />
</div>
</div>
<!-- 邮箱 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<input class="weui-input" type="email" name="contact_email" placeholder="邮箱" required maxlength="50" />
</div>
</div>
<!-- 电话 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<input class="weui-input" type="text" name="contact_phone" placeholder="联系电话" required maxlength="15" />
</div>
</div>
<!-- 备注信息 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<textarea class="weui-textarea" name="extra_info" rows="2" placeholder="其他说明(选填)"></textarea>
</div>
</div>
<!-- 协议同意 -->
<div style="padding: 1em;">
<div style="display: flex; align-items: center; gap: 0.5em; font-size: 0.8em; flex-wrap: nowrap;">
<input type="checkbox" name="agree" value="1" required style="width: 1.1em !important; height: 1.1em !important;">
<span class="weui-agree__text" style="line-height: 1.6; flex: 1; word-break: break-word;">
我已阅读并同意
<a href="plugin.php?id=xigua_houseinsurance&mod=protocol&doc=terms" style="color: #337ab7; text-decoration: none;">《服务协议》</a>
和
<a href="plugin.php?id=xigua_houseinsurance&mod=protocol&doc=privacy" style="color: #337ab7; text-decoration: none;">《隐私协议》</a>
</span>
</div>
</div>
<!-- 提交按钮 -->
<div class="weui-btn-area" style="margin: 0.1em auto 0.1em !important;">
<button type="submit" class="weui-btn weui-btn_primary" aria-label="提交订单">提交订单</button>
</div>
<!-- 优惠通知入口 -->
<div style="text-align: center; margin-top: 1em;">
<a href="plugin.php?id=xigua_houseinsurance&mod=promotion" class="weui-btn weui-btn_default">📢 查看最新优惠</a>
</div>
<!-- 办理须知公告栏 -->
<div style="
margin: 0.2em auto 0.5em;
padding: 0.5em 0.5em;
max-width: 95%;
font-size: 0.6rem;
color: #555;
background-color: #f8f8f8;
border-radius: 2px;
line-height: 1.8;
">
<strong style="display:block; margin-bottom: 0.1em;">办理须知:</strong>
1. 我们是专业的保险服务商,已有 10+ 年行业经验,与多家大型保险公司合作,提供高质量的服务,信誉卓著。<br>
2. 您提交订单后,我们的客服人员会根据您情况会通过58平台站内信和邮箱给你提供最合适和最优惠的方案,请留意信息。<br>
3. 如需进一步的咨询,请直接联系客服Rock:<br>
手机号:<a href="tel:9022774019" style="color: #007aff; text-decoration: none;">9022774019</a><br>
微信号:<a href="javascript:void(0);" onclick="showWeChatQRCode()" style="color: #007aff; text-decoration: none;">Mike291314</a>
</div>
</form>
</div>
<!-- 弹出层 HTML -->
<div id="wechatModal">
<div id="wechatModalContent">
<span id="closeModal" onclick="closeWeChatQRCode()">×</span>
<p style="margin-bottom: 8px;">长按二维码或扫码添加微信</p>
<img src="source/plugin/xigua_simtelecom/template/static/CSWechatBQ.png" alt="微信二维码">
</div>
</div>
<!-- 错误提示容器:样式简单但兼容性强,支持移动端 -->
<div id="toast" style="
display: none;
background: #ff552e;
color: white;
padding: 10px;
border-radius: 5px;
position: fixed;
top: 20%;
left: 50%;
transform: translateX(-50%);
z-index: 9999;
max-width: 80%;
font-size: 14px;
text-align: center;">
<p id="toastMsg"></p>
</div>
<!-- 前端校验逻辑增强版+ 二维码控制弹出与关闭-->
<script>
document.addEventListener('DOMContentLoaded', function () {
const btn = document.querySelector('.weui-btn_primary');
const form = document.querySelector('.weui-form');
const toast = document.getElementById('toast');
const toastMsg = document.getElementById('toastMsg');
function escapeHtml(str) {
return str.replace(/[&<>'"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
}
// 提示框安全转义输出
function showToast(msg) {
toastMsg.innerHTML = escapeHtml(msg);
toast.style.display = 'block';
setTimeout(() => { toast.style.display = 'none'; }, 2000);
}
// 表单按钮点击绑定校验逻辑
btn.addEventListener('click', function () {
// 防止重复提交(4秒内锁定按钮)
if (btn.disabled) return;
btn.disabled = true;
setTimeout(() => { btn.disabled = false; }, 4000);
const pkg = form.house_type.value.trim();
const area = form.house_area.value.trim();
const address = form.address.value.trim();
const name = form.contact_name.value.trim();
const email = form.contact_email.value.trim();
const phone = form.contact_phone.value.trim();
const agree = form.querySelector('input[name="agree"]').checked;
const phoneReg = /^(1[3-9]\d{9}|[2-9]\d{2}[2-9]\d{2}\d{4})$/;
const emailReg = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
if (!pkg) { showToast('请选择房屋类型'); return; }
if (!area) { showToast('请输入房屋面积(数字)'); return; }
if (!address || address.length > 100) { showToast('请填写地址(不超过100字)'); return; }
if (!name) { showToast('请输入您的姓名'); return; }
if (!email || !emailReg.test(email)) { showToast('请输入正确邮箱'); return; }
if (!phone || !phoneReg.test(phone)) { showToast('请输入有效手机号'); return; }
if (!agree) { showToast('请勾选协议'); return; }
// 只在校验通过后提交表单
form.submit();
});
});
<!-- 微信二维码控制弹出与关闭 -->
function showWeChatQRCode() {
document.getElementById('wechatModal').style.display = "block";
}
function closeWeChatQRCode() {
document.getElementById('wechatModal').style.display = "none";
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-cmn-Hans" class="pixel-ratio-2 retina ios">
<head>
<meta charset="UTF-8">
<!-- viewport-fit=cover 可适配 iPhone 刘海屏安全区域 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>提交成功 - 58海外</title>
<!-- 引入自定义样式,内含 weui 组件定义 -->
<link rel="stylesheet" href="source/plugin/xigua_houseinsurance/template/static/custom.css">
</head>
<body>
<!-- 页头公共模板(包含返回按钮和标题) -->
<!--{template xigua_houseinsurance:touch/common_header}-->
<!-- 主体提示区域,使用 WeUI 的标准信息提示样式 .weui-msg -->
<div class="weui-msg" style="margin-top: 3em;">
<!-- 成功图标区域(大尺寸) -->
<div class="weui-msg__icon-area">
<i class="weui-icon-success weui-icon_msg"></i>
</div>
<!-- 标题与描述信息 -->
<div class="weui-msg__text-area">
<h2 class="weui-msg__title">提交成功</h2>
<p class="weui-msg__desc">
我们已收到您的办理申请,客服将跟进处理!<br />
<span id="sec">5</span> 秒后将自动返回。
</p>
</div>
<!-- 操作按钮区域 -->
<div class="weui-msg__opr-area">
<p class="weui-btn-area">
<a href="plugin.php?id=xigua_houseinsurance" class="weui-btn weui-btn_primary">我已知晓</a>
</p>
</div>
</div>
<!-- 自动跳转倒计时逻辑:每秒递减,到 0 时跳转回办理页 -->
<script>
document.addEventListener('DOMContentLoaded', function () {
let counter = 5;
let sec = document.getElementById('sec');
let timer = setInterval(() => {
counter--;
sec.textContent = counter;
if (counter === 0) {
clearInterval(timer);
window.location.href = 'plugin.php?id=xigua_houseinsurance';
}
}, 1000);
});
</script>
</body>
</html>
<!-- 页面顶部头部栏 -->
<header class="x_header bgcolor_11 cl f15" style="background-color:#ff552e !important;">
<!-- 左侧返回按钮,使用浏览器后退功能 -->
<a class="z f14" href="javascript:window.history.go(-1);" style="color: white;">
<span style="font-size: 1.3em; position: relative; top: -1px;">‹</span> 返回
</a>
<!-- 右侧“首页”图标按钮 -->
<a class="y sidectrl" href="plugin.php?id=xigua_hb" style="color:#fff;">
首页 <span style="font-size: 1.3em; position: relative;">⌂</span>
</a>
<!-- 中间标题文本,显示当前页面名称 -->
<div class="navtitle">保险办理</div>
</header>
<template name="common/header" />
<div class="order-detail-container">
<h2 class="title">房屋保险订单详情</h2>
<div class="order-block">
<p><strong>订单号:</strong>{$order['order_no']}</p>
<p><strong>联系人:</strong>{$order['contact_name']}</p>
<p><strong>联系电话:</strong>{$order['phone']}</p>
<p><strong>房屋地址:</strong>{$order['address']}</p>
<p><strong>提交时间:</strong>{$order['created_at']}</p>
</div>
<div class="quote-section">
<h3>客服报价信息</h3>
<!--{if $order['quote_amount'] > 0}-->
<p><strong>报价金额:</strong>¥{$order['quote_amount']}</p>
<p><strong>报价说明:</strong>{$order['quote_desc']}</p>
<p><strong>报价时间:</strong>{$order['quote_time']}</p>
<!--{if $order['status'] == 1}-->
<form method="post" action="plugin.php?id=xigua_houseinsurance&op=confirm_payment">
<input type="hidden" name="formhash" value="{FORMHASH}">
<input type="hidden" name="orderid" value="{$order['id']}">
<div class="btn-fixed">
<button type="submit" name="confirmpay" class="btn-confirm">✅ 同意报价并办理</button>
</div>
</form>
<!--{elseif $order['status'] == 2}-->
<p class="status-info">您已完成支付,等待商户处理。</p>
<!--{elseif $order['status'] == 3}-->
<p class="status-info">业务已完成,查看保险单据。</p>
<!--{/if}-->
<!--{else}-->
<p>暂未收到客服报价,请稍候刷新页面查看。</p>
<!--{/if}-->
</div>
</div>
<template name="common/header" />
<div class="order-list-container">
<h2>我的房屋保险订单</h2>
<!--{if $orders}-->
<!--{loop $orders $order}-->
<div class="order-card">
<p><strong>订单号:</strong>{$order['order_no']}</p>
<p><strong>状态:</strong>{$order_status[$order['status']]}</p>
<p><strong>时间:</strong>{$order['created_at']}</p>
<a href="plugin.php?id=xigua_houseinsurance&op=detail&orderid={$order['id']}">查看详情</a>
</div>
<!--{/loop}-->
<!--{else}-->
<p>暂无订单记录。</p>
<!--{/if}-->
</div>
<!-- 展示优惠内容 -->
<div class="promotion-content">
<!--{eval echo $_G['setting']['plugins']['xigua_houseinsurance']['insurance_promotions'];}-->
</div>
<!DOCTYPE html>
<!-- 保留原有多系统 class,可用于响应式样式控制 -->
<html lang="zh-cmn-Hans" class="pixel-ratio-2 retina ios ios-16 ios-16-6 ios-gt-15 ios-gt-14 ios-gt-13 ios-gt-12 ios-gt-11 ios-gt-10 ios-gt-9 ios-gt-8 ios-gt-7 ios-gt-6">
<head>
<meta charset="UTF-8">
<!-- viewport-fit=cover 可配合安全区域适配 iOS 刘海屏 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>58海外</title>
<link rel="stylesheet" href="source/plugin/xigua_simtelecom/template/static/custom.css">
<style>
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
background-color: #f5f5f5;
color: #333;
}
.content {
padding: 2.5rem 1rem 6rem 1rem;
max-width: 720px;
margin: 0 auto;
line-height: 1.5;
background: #fff;
}
h1, h2 {
font-size: 0.8rem;
margin: 0.6rem 0 0.4rem;
}
p {
font-size: 0.6rem;
margin: 0.4rem 0;
}
.btn-fixed {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: #ffffff;
padding: 0.4rem 0;
box-shadow: 0 -2px 6px rgba(0,0,0,0.06);
text-align: center;
}
.btn-confirm {
background-color: #28a745;
color: white;
font-size: 1rem;
padding: 0.4rem 1rem;
border: none;
border-radius: 5px;
width: 60%;
max-width: 300px;
}
</style>
</head>
<body>
<!--{template xigua_simtelecom:touch/common_header}-->
<div class="content">
<h1 style="text-align: center; font-weight: bold;">隐私协议</h1>
<section>
<p>本平台非常重视您的个人信息保护。本隐私协议旨在说明我们如何收集、使用和保护您提交的信息。</p>
<h2>1. 信息收集范围</h2>
<p>我们收集的信息包括但不限于:姓名、电话号码、电子邮箱、地址等;您提交的内容仅用于协助您完成通信服务的申请办理。</p>
<h2>2. 信息使用目的</h2>
<p>用于核实办理身份、提交至通信运营商开户;用于客服与您取得联系,处理办理流程中的相关事宜;用于改善服务质量和平台使用体验(非营销用途)。</p>
<h2>3. 信息保护措施</h2>
<p>平台采用 HTTPS 加密技术、服务器访问权限控制等方式保护您的信息;未经授权,我们不会向任何无关第三方披露您的信息,除非符合法律规定或获得您明确授权。</p>
<h2>4. 第三方合作方</h2>
<p>提交的信息可能提供给电信服务商用于开户;合作方有义务对您的信息保密并仅用于服务目的。</p>
<h2>5. 用户权利</h2>
<p>您有权随时撤销您提交的信息;可通过平台客服申请信息删除,前提是不影响已完成业务的必要记录。</p>
</div>
<div class="btn-fixed">
<button class="btn-confirm" onclick="window.history.back();">确定</button>
</div>
</body>
</html>
<!DOCTYPE html>
<!-- 保留原有多系统 class,可用于响应式样式控制 -->
<html lang="zh-cmn-Hans" class="pixel-ratio-2 retina ios ios-16 ios-16-6 ios-gt-15 ios-gt-14 ios-gt-13 ios-gt-12 ios-gt-11 ios-gt-10 ios-gt-9 ios-gt-8 ios-gt-7 ios-gt-6">
<head>
<meta charset="UTF-8">
<!-- viewport-fit=cover 可配合安全区域适配 iOS 刘海屏 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>58海外</title>
<link rel="stylesheet" href="source/plugin/xigua_simtelecom/template/static/custom.css">
<style>
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
background-color: #f5f5f5;
color: #333;
}
.content {
padding: 2.5rem 1rem 6rem 1rem;
max-width: 720px;
margin: 0 auto;
line-height: 1.5;
background: #fff;
}
h1, h2 {
font-size: 0.8rem;
margin: 0.6rem 0 0.4rem;
}
p {
font-size: 0.6rem;
margin: 0.4rem 0;
}
.btn-fixed {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: #ffffff;
padding: 0.4rem 0;
box-shadow: 0 -2px 6px rgba(0,0,0,0.06);
text-align: center;
}
.btn-confirm {
background-color: #28a745;
color: white;
font-size: 1rem;
padding: 0.4rem 1rem;
border: none;
border-radius: 5px;
width: 60%;
max-width: 300px;
}
</style>
</head>
<body>
<!--{template xigua_simtelecom:touch/common_header}-->
<div class="content">
<h1 style="text-align: center; font-weight: bold;">办理协议</h1>
<p>欢迎使用 58海外平台入驻服务商联合电信的移动通信服务申请功能。本协议适用于您通过平台提交信息办理加拿大通信套餐的相关操作。</p>
<h2>1. 服务说明</h2>
<p>本平台为您提供加拿大本地电信运营商的套餐申请提交服务;所有套餐服务由相关第三方加拿大本地大型电信运营商提供与履约;本平台不直接提供通信服务,仅作为信息传递中介。</p>
<h2>2. 用户义务</h2>
<p>您需确保提交的信息(姓名、联系方式、地址等)真实、完整、有效;若因信息不实或不完整导致无法办理或服务中断,由用户自行承担责任;禁止任何恶意提交、重复无效申请、冒用他人信息等行为。</p>
<h2>3. 申请流程说明</h2>
<p>成功提交后,平台客服将在 1 个工作日内与您联系核实;核实通过后,您的申请将转交通信运营商进行办理;办理周期及服务内容以运营商最终确认为准。</p>
<h2>4. 费用与支付</h2>
<p>如套餐涉及月费或一次性费用,均以电信服务商和电信运营商说明为准;您需按运营商要求完成开户与付款流程,本平台不代收任何通信费用。</p>
<h2>5. 法律适用</h2>
<p>本协议适用加拿大联邦及所在省份相关法律法规;如发生争议,您可与本平台客服联系协调解决。</p>
</div>
<div class="btn-fixed">
<button class="btn-confirm" onclick="window.history.back();">确定</button>
</div>
</body>
</html>
- 响应式与移动端优化
- 使用 保证适配;
- 所有控件宽度使用 width: 100%,按钮使用固定 padding 提高点击体验;
- 样式集中定义于 custom.css,避免内联混乱。
3.5 主控制器开发¶
- 文件路径:
source/plugin/xigua_houseinsurance/xigua_houseinsurance.inc.php - 核心职责:
- 判断是否为表单提交请求;
- 校验用户登录状态;
- 验证表单字段;
- 构造订单数据、生成唯一订单号;
- 写入数据表 hwxigua_houseinsurance_order;
- 跳转至提交成功页面。
<?php
if (!defined('IN_DISCUZ')) {
exit('Access Denied');
}
// 引入数据封装类
require_once DISCUZ_ROOT.'./source/plugin/xigua_houseinsurance/table/table_xigua_houseinsurance_order.php';
// 协议跳转
if ($_GET['mod'] == 'protocol' && in_array($_GET['doc'], ['terms', 'privacy'])) {
$tpl = "touch/protocol_" . $_GET['doc'];
include template("xigua_houseinsurance:$tpl");
exit;
}
// 提交表单
if ($_SERVER['REQUEST_METHOD'] === 'POST' && submitcheck('ordersubmit')) {
$uid = $_G['uid'];
if (!$uid) {
showmessage('请登录后再提交订单', '', array(), array('login' => 1));
}
$data = array(
'uid' => $uid,
'house_type' => dhtmlspecialchars(trim($_POST['house_type'])),
'house_area' => dhtmlspecialchars(trim($_POST['house_area'])),
'address' => dhtmlspecialchars(trim($_POST['address'])),
'contact_name' => dhtmlspecialchars(trim($_POST['contact_name'])),
'contact_email' => dhtmlspecialchars(trim($_POST['contact_email'])),
'contact_phone' => dhtmlspecialchars(trim($_POST['contact_phone'])),
'extra_info' => dhtmlspecialchars(trim($_POST['extra_info'])),
'created_at' => date('Y-m-d H:i:s'),
'status' => 0
);
// 基本字段校验
foreach(['house_type', 'house_area', 'address', 'contact_name', 'contact_email', 'contact_phone'] as $field) {
if (empty($data[$field])) {
showmessage('请完整填写所有必填项');
}
}
if (!filter_var($data['contact_email'], FILTER_VALIDATE_EMAIL)) {
showmessage('请输入有效邮箱');
}
if (!preg_match('/^(1[3-9]\d{9}|[2-9]\d{2}[2-9]\d{2}\d{4})$/', $data['contact_phone'])) {
showmessage('请输入有效手机号(不含国家区号)');
}
// 生成唯一订单号:UID + biz_type + 时间戳 + 随机2位
$data['order_no'] = $uid . 'houseinsurance' . date('YmdHis') . mt_rand(10, 99);
// 使用对象方式调用数据封装类插入
$table = new table_xigua_houseinsurance_order();
$insert_id = $table->insert($data);
// 发送通知邮件至客服
include_once libfile('function/mail');
// 当前时间格式化
$time_str = date('Y-m-d H:i:s');
$message_body = <<<EOF
有一位用户提交了房屋保险业务申请,信息如下:<br>
姓名:{$data['contact_name']}<br>
手机号:{$data['contact_phone']}<br>
邮箱:{$data['contact_email']}<br>
房子类型:{$data['house_type']}<br>
房子面积:{$data['house_area']}<br>
地址:{$data['address']}<br>
提交时间:{$time_str}<br>
备注:{$data['extra_info']}<br>
用户IP:{$_G['clientip']}<br>
UID/用户名:{$_G['uid']} / {$_G['username']}<br>
EOF;
sendmail('284641883@qq.com', '【新订单通知】房屋保险业务申请提交成功', $message_body);
//sendmail('284641883@qq.com,chuxing9527@gmail.com', '【新订单通知】房屋保险业务申请提交成功', $message_body);
if ($insert_id) {
$tpl = checkmobile() ? 'touch/success_dialog' : 'success_dialog';
include template("xigua_houseinsurance:$tpl");
exit;
} else {
showmessage('订单提交失败,请稍后再试');
exit;
}
}
// 用户确认报价并办理
elseif ($_GET['op'] == 'confirm_payment' && submitcheck('confirmpay')) {
$orderid = intval($_POST['orderid']);
$table = new table_xigua_houseinsurance_order();
$order = $table->fetch($orderid);
if (!$order || $order['uid'] != $_G['uid']) {
showmessage('订单不存在或无访问权限');
}
if ($order['status'] != 1) {
showmessage('订单状态异常,无法确认办理');
}
$table->update($orderid, array(
'status' => 2, // 用户已确认办理,待支付
'confirm_time' => dgmdate(TIMESTAMP, 'Y-m-d H:i:s')
));
showmessage('您已确认报价,等待支付处理', 'plugin.php?id=xigua_houseinsurance&op=detail&orderid=' . $orderid);
}
// 用户订单历史列表
elseif ($_GET['op'] == 'list') {
$uid = $_G['uid'];
if (!$uid) {
showmessage('请登录后查看订单记录', '', array(), array('login' => 1));
}
$table = new table_xigua_houseinsurance_order();
$orders = $table->fetch_by_uid($uid);
include template('xigua_houseinsurance:touch/order_list');
}
// 默认访问行为(展示提交表单)
else {
$tpl = checkmobile() ? 'touch/front_order_form' : 'front_order_form';
include template("xigua_houseinsurance:$tpl");
}
- 说明:
- 使用 submitcheck('formsubmit', 1) 判断 POST 提交;
- 订单号采用组合生成,确保唯一;
- 采用 C::t('#pluginid#tablename') 插件表封装方式操作数据库;
- 成功后使用 dheader() 跳转至 success_dialog.htm 页面;
- 未提交则直接渲染表单页面模板。
3.6 数据封装类设计¶
- 文件路径:
source/plugin/xigua_houseinsurance/table/table_xigua_houseinsurance_order.php
<?php
if (!defined('IN_DISCUZ')) {
exit('Access Denied');
}
/**
* 房屋保险订单数据操作封装类
* 表名:hwxigua_houseinsurance_order
*/
class table_xigua_houseinsurance_order extends discuz_table {
public function __construct() {
$this->_table = 'xigua_houseinsurance_order'; // 数据表名 // 生产环境自动加前缀 hw
$this->_pk = 'id'; // 主键字段
parent::__construct();
}
/**
* 插入新订单
* @param array $data 订单数据数组
* @param bool $return_insert_id 是否返回自增ID
* @return int|bool
*/
public function insert($data, $return_insert_id = false, $replace = false, $silent = false) {
return parent::insert($data, $return_insert_id, $replace, $silent);
}
/**
* 获取用户所有订单(按时间倒序)
* @param int $uid 用户UID
* @param int $limit 最大条数
* @param string $order 排序方式
* @return array
*/
public function fetch_by_uid($uid, $limit = 50, $order = 'DESC') {
return DB::fetch_all("SELECT * FROM %t WHERE uid=%d ORDER BY created_at $order LIMIT %d",
array($this->_table, $uid, $limit));
}
/**
* 根据唯一订单号获取订单记录
* @param string $order_no
* @return array|null
*/
public function fetch_by_order_no($order_no) {
return DB::fetch_first("SELECT * FROM %t WHERE order_no=%s", array($this->_table, $order_no));
}
/**
* 更新订单报价信息
* @param int $id 订单ID
* @param float $quote_amount 报价金额
* @param string $quote_desc 报价说明
* @return int 更新行数
*/
public function update_quote($id, $quote_amount, $quote_desc) {
return DB::update($this->_table, array(
'quote_amount' => $quote_amount,
'quote_desc' => dhtmlspecialchars($quote_desc),
'status' => 1, // 状态:已报价
'quote_time' => date('Y-m-d H:i:s')
), "id=%d", array($id));
}
/**
* 标记订单为已支付
* @param int $id 订单ID
* @return int 更新行数
*/
public function mark_as_paid($id) {
return DB::update($this->_table, array(
'status' => 2, // 状态:已支付
'paid_time' => date('Y-m-d H:i:s')
), "id=%d", array($id));
}
/**
* 上传完成资料(保险单据)并更新状态
* @param int $id 订单ID
* @param string $filename 文件名(相对路径)
* @return int 更新行数
*/
public function upload_insurance_doc($id, $filename) {
return DB::update($this->_table, array(
'status' => 3, // 状态:已完成
'insurance_file' => dhtmlspecialchars($filename),
'completed_at' => date('Y-m-d H:i:s')
), "id=%d", array($id));
}
}
- 说明:
- 支持按 UID、订单号查询;
- 支持新增、报价、支付、完成操作;
- 所有时间字段均使用 Y-m-d H:i:s 格式;
- 所有更新操作使用 update_by_pk 封装形式;
- 插件目录名 xigua_houseinsurance 要与 class 前缀保持一致。
3.7 多语言包支持¶
- 文件路径:
source/plugin/xigua_houseinsurance/xigua_houseinsurance.lang.php
<?php
return array(
'plugin/xigua_houseinsurance:house_type' => '房屋类型',
'plugin/xigua_houseinsurance:house_area' => '房屋面积',
'plugin/xigua_houseinsurance:address' => '房产地址',
'plugin/xigua_houseinsurance:contact_name' => '联系人姓名',
'plugin/xigua_houseinsurance:contact_email' => '联系邮箱',
'plugin/xigua_houseinsurance:contact_phone' => '联系电话',
'plugin/xigua_houseinsurance:extra_info' => '其他说明',
'plugin/xigua_houseinsurance:submit' => '提交订单',
'plugin/xigua_houseinsurance:success_tip' => '我们已收到您的申请,客服将尽快联系您报价',
'plugin/xigua_houseinsurance:order_status_0' => '待报价',
'plugin/xigua_houseinsurance:order_status_1' => '待支付',
'plugin/xigua_houseinsurance:order_status_2' => '已支付,待完成',
'plugin/xigua_houseinsurance:order_status_3' => '已完成',
);
3.8 安装脚本 install.php¶
<?php
if (!defined('IN_DISCUZ')) {
exit('Access Denied');
}
$sql = <<<EOF
CREATE TABLE IF NOT EXISTS `hwxigua_houseinsurance_order` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uid` INT(10) UNSIGNED NOT NULL COMMENT '用户ID',
`order_no` VARCHAR(64) NOT NULL UNIQUE COMMENT '订单编号',
`house_type` VARCHAR(32) NOT NULL,
`house_area` VARCHAR(20) DEFAULT '',
`address` VARCHAR(255) NOT NULL,
`contact_name` VARCHAR(60) NOT NULL,
`contact_email` VARCHAR(100) NOT NULL,
`contact_phone` VARCHAR(30) NOT NULL,
`extra_info` TEXT,
`quote_amount` DECIMAL(10,2) DEFAULT NULL,
`quote_desc` TEXT,
`quote_time` DATETIME DEFAULT NULL,
`status` TINYINT DEFAULT 0 COMMENT '订单状态:0待报价,1待支付,2已支付,3已完成',
`paid_time` DATETIME DEFAULT NULL,
`insurance_file` VARCHAR(255) DEFAULT NULL COMMENT '保险单文件路径',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`completed_at` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
EOF;
runquery($sql);
$finish = true;
3.9 卸载脚本 uninstall.php¶
<?php
if (!defined('IN_DISCUZ')) {
exit('Access Denied');
}
// 若不希望删除历史数据,可将 DROP 语句注释掉
$sql = <<<EOF
DROP TABLE IF EXISTS `hwxigua_houseinsurance_order`;
EOF;
runquery($sql);
$finish = true;
3.10 后台变量定义¶
- 插件后台变量用于在不修改代码的情况下,供管理员灵活配置“优惠通知”内容。定义在 plugin.xml 中
部分,类型为 textarea,支持 HTML:
<settings>
<item id="insurance_promotions" type="textarea" rows="8" cols="80">
<title>优惠通知内容</title>
<description>填写将展示在前台“优惠通知”页面的内容,支持 HTML。</description>
<default><![CDATA[<h3>本月房屋保险优惠政策</h3><p>本月投保最高立减 200 元,详情请联系客服。</p>]]></default>
</item>
</settings>
在模板 promotion_notice.htm 中,可使用:
<!-- 展示优惠内容 -->
<div class="promotion-content">
<!--{eval echo $_G['setting']['plugins']['xigua_houseinsurance']['insurance_promotions'];}-->
</div>
- 说明:
- 可直接在后台插件设置页面填写图文内容;
- 支持嵌入 HTML 元素、图标、超链接等。
3.11 后台插件管理页子插件¶
如需在 Discuz 后台插件管理页面中添加额外的子菜单页签,可使用
目前 xigua_houseinsurance 插件主要后台交互通过 xigua_partnercenter,无需设置后台页签。但如未来需要将部分管理迁入插件自身后台,可配置如下:
<subplugins>
<subplugin>
<name>订单管理</name>
<url>plugin.php?id=xigua_houseinsurance:admin_order</url>
</subplugin>
</subplugins>
- 此方式适用于:
- 后续不使用 partnercenter 的独立运营模式;
- 需要提供管理员总览、报表、导出等高级功能页面。
4. 商户管理中心扩展(xigua_partnercenter)¶
为了统一各业务插件(如 simtelecom、houseinsurance 等)在商户后台的订单管理能力,xigua_houseinsurance 插件需提供专用的模板与控制器,以接入 xigua_partnercenter 统一入口:
4.1 插件订单模板模板¶
- 文件路径建议:
source/plugin/xigua_partnercenter/template/touch/houseinsurance_order_manager.htm - 该模板用于渲染房屋保险业务订单列表,推荐继承自通用模板 order_list.htm,并通过传入业务参数渲染字段。模板内容可参考如下结构:
<!DOCTYPE html>
<!-- 保留原有多系统 class,可用于响应式样式控制 -->
<html lang="zh-cmn-Hans" class="pixel-ratio-2 retina ios ios-16 ios-16-6 ios-gt-15 ios-gt-14 ios-gt-13 ios-gt-12 ios-gt-11 ios-gt-10 ios-gt-9 ios-gt-8 ios-gt-7 ios-gt-6">
<head>
<meta charset="UTF-8">
<!-- viewport-fit=cover 可配合安全区域适配 iOS 刘海屏 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>58海外</title>
<!-- 自适应样式:custom.css 提供移动端样式 -->
<link rel="stylesheet" href="source/plugin/xigua_partnercenter/template/static/custom.css?t={$_G['timestamp']}">
<style>
.btn-fixed {
margin-top: 16px;
text-align: center;
}
.btn-confirm {
display: inline-block;
background-color: #07c160; /* 微信绿 */
color: white;
font-size: 16px;
padding: 10px 40px;
border: none;
border-radius: 4px;
width: 90%;
max-width: 300px;
box-shadow: 0 2px 5px rgba(0,0,0,0.15);
}
.weui-cell {
padding: 6px 9px;
background: #fff;
}
/* 标签提示文字 */
.label {
color: #666;
font-weight: 500;
font-size: 15px;
}
.value {
color: #333;
font-weight: 400;
font-size: 15px;
}
/* 自动高度备注框 */
.weui-textarea.autoexpand {
line-height: 1.5;
padding: 6px 8px;
resize: none;
font-size: 15px;
border: none;
background-color: #fff;
width: 100%;
}
.weui-label {
line-height: 1.5 !important;
font-size: 15px;;
}
.weui-textarea.autoexpand {
line-height: 1.5 !important;
padding: 6px 8px !important;
font-size: 15px;
}
.weui-cell {
align-items: center !important; /* 确保 cell 内元素垂直居中 */
}
.weui-cell__hd,
.weui-cell__bd {
display: flex
align-items: center
}
.status-pending {
color: #e64545 !important; /* 红色 */
}
.status-doing {
color: #f39800 !important; /* 橙色 */
}
.status-done {
color: #28a745 !important; /* 绿色 */
}
.status-cancel {
color: #999 !important; /* 灰色 */
}
</style>
</head>
<body>
<!-- 页面顶部头部栏 -->
<header class="x_header bgcolor_11 cl f15" style="background-color:#ff552e !important;">
<a class="z f14" href="javascript:window.history.go(-1);" style="color: white;">
<span style="font-size: 1.3em; position: relative; top: -1px;">‹</span> 返回
</a>
<a class="y sidectrl" href="plugin.php?id=xigua_houseinsurance" style="color:#fff;">
首页 <span style="font-size: 1.3em; position: relative;">⌂</span>
</a>
<div class="navtitle">房屋保险订单管理</div>
</header>
<!-- 页面主体容器 -->
<div class="xg-container" style="padding-top: 2.2rem;">
<!-- ✅ 筛选表单 -->
<form method="get" class="xg-form xg-card" style="margin-bottom: 1rem;">
<input type="hidden" name="id" value="xigua_partnercenter">
<input type="hidden" name="biz" value="xigua_houseinsurance">
<div class="weui-cell"><div class="weui-cell__bd">
<input class="weui-input" type="text" name="contact_name" placeholder="联系人姓名" value="{$_GET['contact_name']}">
</div></div>
<div class="weui-cell"><div class="weui-cell__bd">
<input class="weui-input" type="text" name="contact_phone" placeholder="联系电话" value="{$_GET['contact_phone']}">
</div></div>
<div class="weui-cell weui-cell_select">
<div class="weui-cell__bd">
<select class="weui-select" name="status">
<option value="">选择状态</option>
<option value="0" {if $_GET['status']=='0'}selected{/if}>待处理</option>
<option value="1" {if $_GET['status']=='1'}selected{/if}>已报价</option>
<option value="2" {if $_GET['status']=='2'}selected{/if}>已确认</option>
<option value="3" {if $_GET['status']=='3'}selected{/if}>已完成</option>
</select>
</div>
</div>
<div class="btn-fixed">
<button type="submit" class="btn-confirm">🔍 查询</button>
</div>
</form>
<!-- ✅ 订单展示与报价填写区域 -->
<!-- ✅ 订单展示与报价填写区域 -->
<form method="post" class="xg-form">
<!--{loop $orders $order}-->
<div class="weui-cells__group weui-cells weui-cells_form"
style="margin-bottom: 1rem; border-radius: 8px; overflow: hidden;
background: #fff;
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
border: 1px solid #e5e5e5;">
<!-- 显示订单基础信息 -->
<div class="weui-cell"><div class="weui-cell__bd">
<p><span class="label">订单号:</span> {$order['order_no']}</p>
<p><span class="label">联系人:</span> {$order['contact_name']}</p>
<p><span class="label">地址:</span> {$order['address']}</p>
<p><span class="label">电话:</span> {$order['contact_phone']}</p>
<p><span class="label">房屋类型:</span> {$order['house_type']}</p>
<p><span class="label">面积:</span> {$order['house_area']}</p>
<p><span class="label">提交时间:</span> {$order['created_at']}</p>
</div></div>
<!-- 选择订单状态 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<label class="label">订单状态:</label>
<select name="status[{$order['id']}]" class="weui-select">
<option value="0" {if $order['status'] == 0}selected{/if}>待处理</option>
<option value="1" {if $order['status'] == 1}selected{/if}>已报价</option>
<option value="2" {if $order['status'] == 2}selected{/if}>客户确认</option>
<option value="3" {if $order['status'] == 3}selected{/if}>已付款</option>
<option value="4" {if $order['status'] == 4}selected{/if}>已完成</option>
</select>
</div>
</div>
<!-- 填写报价金额 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<label class="label">报价金额(CAD):</label>
<input type="number" name="quote_price[{$order['id']}]" class="weui-input"
value="{$order['quote_price']}" step="0.01" placeholder="如:120.00">
</div>
</div>
<!-- 填写报价说明 -->
<div class="weui-cell">
<div class="weui-cell__bd">
<label class="label">报价说明:</label>
<textarea name="quote_remark[{$order['id']}]" class="weui-textarea" rows="3"
placeholder="例如:保险范围、免责条款等说明">{$order['quote_remark']}</textarea>
</div>
</div>
</div>
<!--{/loop}-->
</div>
</body>
</html>
4.2 子控制器接口¶
- 统一通过 plugin.php?id=xigua_partnercenter&biz=xigua_houseinsurance 访问,处理订单查询、报价提交、上传保险单等逻辑。支持以下参数接口:
- op=list 默认显示订单列表
- op=detail&orderid=xxx 进入单个订单查看与报价提交页
- op=quote&orderid=xxx POST 提交报价数据
- op=upload&orderid=xxx 上传保险单据并更新订单状态
- 核心逻辑:
<?php
if (!defined('IN_DISCUZ')) {
exit('Access Denied');
}
$biz = 'xigua_houseinsurance';
$op = $_GET['op'] ?: 'list';
$uid = $_G['uid'];
$table = C::t('#xigua_houseinsurance#xigua_houseinsurance_order');
if ($op == 'list') {
$orders = $table->fetch_by_uid($uid);
include template('xigua_partnercenter:touch/houseinsurance_order_manager');
exit;
} elseif ($op == 'detail' && $_GET['orderid']) {
$orderid = intval($_GET['orderid']);
$order = $table->fetch($orderid);
include template('xigua_partnercenter:touch/houseinsurance_order_detail');
exit;
} elseif ($op == 'quote' && submitcheck('quotesubmit')) {
$orderid = intval($_POST['orderid']);
$quote_amount = floatval($_POST['quote_amount']);
$quote_desc = dhtmlspecialchars($_POST['quote_desc']);
$table->update($orderid, array(
'quote_amount' => $quote_amount,
'quote_desc' => $quote_desc,
'quote_time' => dgmdate(TIMESTAMP, 'Y-m-d H:i:s'),
'status' => 1, // 状态:已报价,待用户确认
));
showmessage('报价提交成功', "plugin.php?id=xigua_partnercenter&biz=$biz");
} elseif ($op == 'upload' && submitcheck('uploadsubmit')) {
$orderid = intval($_POST['orderid']);
if ($_FILES['policy_file']['error'] === 0) {
$filename = 'data/attachment/houseinsurance/' . TIMESTAMP . '_' . $_FILES['policy_file']['name'];
if (move_uploaded_file($_FILES['policy_file']['tmp_name'], DISCUZ_ROOT . '/' . $filename)) {
$table->upload_insurance_doc($orderid, $filename);
showmessage('保险文件上传成功', "plugin.php?id=xigua_partnercenter&biz=$biz");
} else {
showmessage('文件上传失败,请重试');
}
} else {
showmessage('未选择有效文件');
}
}
4.3 xigua_partnercenter插件的主控制器调整¶
将 xigua_houseinsurance 加入白名单数组,修改如下:
// 原来的代码
$allowed_biz = array('simtelecom'); // 白名单业务(可扩展)
if (!in_array($biz, $allowed_biz)) {
showmessage('业务类型非法或尚未开通');
}
// 增加xigua_houseinsurance业务
$allowed_biz = array('simtelecom', 'xigua_houseinsurance'); // 添加 houseinsurance 支持
创建业务注册表 hwxigua_partnercenter_app
CREATE TABLE IF NOT EXISTS `hwxigua_partnercenter_app` (
`biz` VARCHAR(30) NOT NULL PRIMARY KEY COMMENT '业务标识(唯一)',
`name` VARCHAR(50) NOT NULL COMMENT '业务显示名称',
`entry` VARCHAR(100) NOT NULL COMMENT '入口控制器路径,相对DISCUZ_ROOT',
`template_prefix` VARCHAR(50) DEFAULT '' COMMENT '模板调用前缀',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5. 插件安装与部署¶
5.1 上传插件代码¶
- 发布版本插件代码路径:
D:\3 Work\58halifax\ProjectDev\RVersion\xigua_houseinsurance - 通过 MobaXterm 拖曳上传至生产环境临时文件夹:
/home/ecs-user/ProjectDev/Rversion/xigua_houseinsurance - 同步至站点插件目录且授予www-data权限
sudo rsync -avz --delete /home/ecs-user/ProjectDev/Rversion/xigua_houseinsurance/ /var/www/html/58haiwaiweb/source/plugin/xigua_houseinsurance/
sudo chown -R www-data:www-data /var/www/html/58haiwaiweb/source/plugin/xigua_houseinsurance
5.2 安装插件¶
- 启用
plugindeveloper模式- 编辑配置文件:
nano /var/www/html/58haiwaiweb/config/config_global.php - 添加配置:
$_config['plugindeveloper'] = 1;
- 编辑配置文件:
- 复制语言包至指定目录
sudo cp /var/www/html/58haiwaiweb/source/plugin/xigua_houseinsurance/xigua_houseinsurance.lang.php /var/www/html/58haiwaiweb/data/plugindata/
- 后台点击"“设计新插件”


- 添加变量"优惠通知内容",类型为 textarea,支持 HTML:
insurance_promotions

- 启用插件

5.3 创建插件数据表¶
- 登录数据库,创建表结构并验证
# 登录数据库:
mysql -u hwdbadmin -p
# 选择数据库:
USE haiwaidata;
# 创建表
CREATE TABLE IF NOT EXISTS `hwxigua_houseinsurance_order` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`order_no` VARCHAR(50) NOT NULL COMMENT '订单编号(唯一)',
`uid` INT(10) UNSIGNED NOT NULL COMMENT '提交用户UID',
`house_type` VARCHAR(50) NOT NULL COMMENT '房屋类型',
`house_area` VARCHAR(30) DEFAULT '' COMMENT '面积信息',
`address` VARCHAR(255) DEFAULT '' COMMENT '房产地址',
`contact_name` VARCHAR(50) DEFAULT '' COMMENT '联系人姓名',
`contact_email` VARCHAR(100) DEFAULT '' COMMENT '邮箱',
`contact_phone` VARCHAR(30) DEFAULT '' COMMENT '联系电话',
`extra_info` TEXT COMMENT '用户备注',
`status` TINYINT DEFAULT 0 COMMENT '订单状态:0待处理,1已报价,2用户确认,3已付款,4已完成',
`quote_price` DECIMAL(10,2) DEFAULT NULL COMMENT '商户填写报价金额',
`quote_remark` TEXT COMMENT '报价说明',
`policy_file` VARCHAR(255) DEFAULT '' COMMENT '上传的保单文件路径',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_order_no` (`order_no`),
KEY `idx_uid` (`uid`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# 验证表是否创建成功
SHOW TABLES LIKE 'hwxigua_houseinsurance_order';
DESCRIBE hwxigua_houseinsurance_order;
5.4 调试与验证¶
- 登录用户授权、表单填写测试、模板渲染、提交跳转
- 添加业务和添加管理员(站长)授权
# 登录数据库:
mysql -u hwdbadmin -p
# 选择数据库:
USE haiwaidata;
# 提交管理员UID=1的授权
INSERT INTO hwxigua_partnercenter_account
(uid, biz_type, permission_level, status, created_at, merchant_type)
VALUES
(1, 'xigua_houseinsurance', 1, 1, NOW(), 1)
ON DUPLICATE KEY UPDATE status = 1;
- 常见问题如模板路径、未exit输出残留
5.5 收尾与总结¶
- 关闭设计模式
- 编写部署笔记与问题记录