Skip to content

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;
  • 订单号字段为系统自动生成的唯一编号,生成规则如下:
order_no = uid + 'houseinsurance' + datetime('YmdHis') + rand(10, 99)

新表结构定义:

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
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()">&times;</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 => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[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>
success_dialog.htm
<!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>
comman_header.php
<!-- 页面顶部头部栏 -->
<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>
order_detail.htm
<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>
order_list.htm
<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>
promotion_notice.htm
<!-- 展示优惠内容 -->
<div class="promotion-content">
  <!--{eval echo $_G['setting']['plugins']['xigua_houseinsurance']['insurance_promotions'];}-->
</div>
protocol_privacy.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_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>
protocol_terms.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_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;
    • 跳转至提交成功页面。
xigua_houseinsurance.inc.php
<?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
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
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

安装脚本 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

卸载脚本 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,并通过传入业务参数渲染字段。模板内容可参考如下结构:
houseinsurance_order_manager.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 上传保险单据并更新订单状态
  • 核心逻辑:
xigua_houseinsurance_order.inc.php
<?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 加入白名单数组,修改如下:

修改xigua_houseinsurance_order.inc.php
// 原来的代码
$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 收尾与总结

  • 关闭设计模式
  • 编写部署笔记与问题记录