Compare commits

..
9 Commits
Author SHA1 Message Date
feng0207 dd663914a1 部署文档补充:主服务器部署与更新流程、重启清库/丢端口等事故经验、密码重置方法 2026-09-26 01:40:41 +08:00
feng0207 0cc56e5509 nginx absolute_redirect off:容器端口映射场景下重定向不再丢失外部端口 2026-09-26 01:29:14 +08:00
feng0207 e9364d2d79 初始化哨兵改为 eb_system_admin 表,防止部分导入的库被误判为已初始化 2026-09-26 00:09:16 +08:00
feng0207 bc40ef3ff8 bullseye EOL 安全包镜像站已清理:构建期去掉 security 源,main/updates 走阿里云 2026-09-25 23:57:40 +08:00
feng0207 2c4446c93f php 镜像 security 源切 TUNA(阿里云 security 同步滞后 404) 2026-09-25 23:51:56 +08:00
feng0207 45283e4e1e php 镜像构建 apt 源切换阿里云镜像(deb.debian.org 从 ECS 超时) 2026-09-25 23:48:25 +08:00
feng0207 9341d27b92 留言页标题去掉商城前缀,改为客服已离线 2026-09-25 22:31:48 +08:00
feng0207 3481572cc0 移除客服页面的 CRMChat 开源客服系统水印链接 2026-09-25 22:25:48 +08:00
feng0207 9e8186e5d3 修复客服嵌入问题并更新前端构建产物
- 管理端前端设备判定改为按宽高比(嵌入竖屏容器时正确使用手机版布局)
- 统计上报跨源读取 parent.location 报错防护(降级用 referrer)
- 续接会话时校验客服在线,离线不再误入聊天页
- 聊天记录整理与消息发送的用户资料缺失空值防护,避免接口 400
- entrypoint 数据库导入加空库守卫,容器重启/线上更新不再重置数据
2026-09-25 22:15:01 +08:00
76 changed files with 182 additions and 108 deletions
+6 -6
View File
@@ -23,15 +23,15 @@
this.handleMatchMedia()
},
handleMatchMedia () {
const matchMedia = window.matchMedia
// 竖屏(宽高比 < 1)走手机版布局,横屏走 PC 布局。
// 嵌入竖屏 iframe(如游戏内客服)时由宿主容器形状决定,不受绝对宽度阈值影响
const w = document.documentElement.clientWidth || window.innerWidth
const h = document.documentElement.clientHeight || window.innerHeight
if (matchMedia('(max-width: 600px)').matches) {
var deviceWidth = document.documentElement.clientWidth || window.innerWidth;
let css = 'calc(100vw/7.5)'
if (h > 0 && w / h < 1) {
const css = 'calc(100vw/7.5)'
document.documentElement.style.fontSize = css;
this.setDevice('Mobile')
} else if (matchMedia('(max-width: 992px)').matches) {
this.setDevice('Tablet')
} else {
this.setDevice('Desktop')
}
+1 -1
View File
@@ -2,7 +2,7 @@
<div>
<div class="feedback" :class="change === true ? 'on' : ''">
<div class="feedback-header acea-row">
<span class="sp1">商城客服已离线</span>
<span class="sp1">客服已离线</span>
<div>
<!--<Icon type="md-remove" color="#fff" size="18" class="mr10"/>-->
<Icon type="md-close" color="#fff" size="18" @click="close"/>
@@ -2,7 +2,7 @@
<div class="feedback-wrapper">
<div class="head">
<div class="left-wrapper">
<div class="title">商城客服已离线</div>
<div class="title">客服已离线</div>
<div class="txt">{{feedback}}</div>
</div>
<div class="img-box"><img src="@/assets/images/feed-icon.png" mode="" /></div>
@@ -1,7 +1,7 @@
<template>
<div class="customerOutLine_server" :class="{ 'max_style': !isMobile }">
<div class="customerOutLine_server_header">
<span>商城客服已离线</span>
<span>客服已离线</span>
<div class="pc_customerServer_container_header_handle" @click="closeIframe">
<span class="iconfont">&#xe6c6;</span>
</div>
@@ -1,7 +1,7 @@
<template>
<div class="customerOutLine_server" :class="{ 'max_style': !isMobile }">
<div class="customerOutLine_server_header">
<span>商城客服已离线</span>
<span>客服已离线</span>
<div class="pc_customerServer_container_header_handle" @click="closeIframe">
<span class="iconfont">&#xe6c6;</span>
</div>
@@ -175,10 +175,17 @@ export default {
} else if (ua.indexOf('Netscape') != -1) {
browser = 'Netscape';
}
// 跨源 iframe 嵌入时读取 parent.location 会抛 SecurityError,降级用 referrer
let source = '';
try {
source = window.parent.location.href;
} catch (e) {
source = window.document.referrer;
}
userStatistics({
ip: window.returnCitySN ? window.returnCitySN.cip : '',
path: window.location.href,
source: window.parent.location.href,
source: source,
browser: browser
});
},
@@ -83,9 +83,6 @@
<div class="footer_customerServer_container">
<div class="mobel_customerServer_container_footer">
<div class="crmchat_link" @click="tolink">
<span>CRMChat开源客服系统</span>
</div>
<div class="mobel_customerServer_container_footer_uploag_image">
<span class="iconfont">&#xe6ca;</span>
<input type="file" class="file_input" @change="uploadFile">
@@ -133,10 +133,6 @@
</div>
</div>
<div class="pc_customerServer_container_footer_copyright" @click="tolink"
v-if="upperData.noCanClose != '1' && upperData.windowStyle != `center`">
<span>CRMChat开源客服系统</span>
</div>
<!-- 相关操作结束 -->
</div>
@@ -147,14 +143,9 @@
v-if="upperData.noCanClose == '1' || upperData.windowStyle == `center`">
<div class="advertisement">
<div v-html="advertisement"></div>
<div class="copyright" @click="tolink">
<span>CRMChat开源客服系统</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
@@ -176,16 +176,17 @@ class ChatServiceDialogueRecordServices extends BaseServices
}
if (!$item['avatar'] && !$item['nickname']) {
if ($item['userThis']['id'] == $item['user_id']) {
// isset 空值防护:chat_user 行缺失时关联为 null,直接取 ['id'] 会抛异常导致整个接口 400
if (isset($item['userThis']['id']) && $item['userThis']['id'] == $item['user_id']) {
$item['nickname'] = $item['userThis']['nickname'] ?? '';
$item['avatar'] = $item['userThis']['avatar'] ?? '';
} else if ($item['userTO']['id'] == $item['user_id']) {
} else if (isset($item['userTO']['id']) && $item['userTO']['id'] == $item['user_id']) {
$item['nickname'] = $item['userTO']['nickname'] ?? '';
$item['avatar'] = $item['userTO']['avatar'] ?? '';
} else if ($item['userTO']['id'] == $item['to_user_id']) {
} else if (isset($item['userTO']['id']) && $item['userTO']['id'] == $item['to_user_id']) {
$item['nickname'] = $item['userTO']['nickname'] ?? '';
$item['avatar'] = $item['userTO']['avatar'] ?? '';
} else if ($item['userThis']['id'] == $item['to_user_id']) {
} else if (isset($item['userThis']['id']) && $item['userThis']['id'] == $item['to_user_id']) {
$item['nickname'] = $item['userThis']['nickname'] ?? '';
$item['avatar'] = $item['userThis']['avatar'] ?? '';
}
@@ -199,8 +199,8 @@ class ChatServiceServices extends BaseServices
}
$userInfo = $userInfo->toArray();
}
//获取当前分配客服
$toUserId = $this->dao->count(['appid' => $appId, 'status' => 1, 'user_id' => $toUserId]) ? $toUserId : 0;
//获取当前分配客服(续接已有会话也要求客服在线,离线时走重新分配/留言逻辑)
$toUserId = $this->dao->count(['appid' => $appId, 'status' => 1, 'online' => 1, 'user_id' => $toUserId]) ? $toUserId : 0;
if (!$toUserId && $kefuId) {
$toUserId = $this->dao->value(['appid' => $appId, 'status' => 1, 'id' => $kefuId], 'user_id');
}
+4 -3
View File
@@ -235,7 +235,8 @@ class KefuServices extends BaseServices
/** @var ChatUserServices $userService */
$userService = app()->make(ChatUserServices::class);
$_userInfo = $userService->getUserInfo($data['user_id'], ['nickname', 'avatar', 'version', 'is_tourist', 'online']);
$isTourist = $_userInfo['is_tourist'];
// 空值兜底:客户端缓存的 user_id 对应的 chat_user 行可能已不存在(如数据重置),缺失时按游客处理,避免发送接口 400
$isTourist = $_userInfo['is_tourist'] ?? 0;
$data['nickname'] = $_userInfo['nickname'] ?? '';
$data['avatar'] = $_userInfo['avatar'] ?? '';
@@ -304,8 +305,8 @@ class KefuServices extends BaseServices
}
if ($fds && ($newUserInfo['to_user_id'] ?? 0) != $userId && $res) {
$data['recored']['nickname'] = $_userInfo['nickname'];
$data['recored']['avatar'] = $_userInfo['avatar'];
$data['recored']['nickname'] = $_userInfo['nickname'] ?? '';
$data['recored']['avatar'] = $_userInfo['avatar'] ?? '';
$data['recored']['online'] = isset($userInfo) ? 1 : 0;
$allUnMessagesCount = $logServices->getMessageNum([
@@ -1 +1 @@
.customerOutLine_server[data-v-6661c7ca]{width:100%;height:100%;position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.customerOutLine_server_header[data-v-6661c7ca]{height:50px;background:-webkit-gradient(linear,right top,left top,from(#1890ff),to(#3875ea));background:linear-gradient(270deg,#1890ff,#3875ea);color:#fff;font-size:16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:0 18px}.customerOutLine_server_header .pc_customerServer_container_header_handle[data-v-6661c7ca]{cursor:pointer}.customerOutLine_server_content[data-v-6661c7ca]{padding:17px 18px}.customerOutLine_server_content_message[data-v-6661c7ca]{font-size:13px;color:#333;margin-bottom:26px}.customerOutLine_server_content_message_phone[data-v-6661c7ca]{margin-top:30px}.customerOutLine_server_content_form>div[data-v-6661c7ca]{margin-bottom:18px}.customerOutLine_server_content_form>div input[data-v-6661c7ca]{border-radius:3px;border:1px solid #ececec;outline:none;font-size:13px;padding-left:14px;width:100%;height:40px}.customerOutLine_server_content_form>div textarea[data-v-6661c7ca]{width:100%;border:1px solid #ececec;outline:none;font-size:13px;padding:14px}.customerOutLine_server_content_handle[data-v-6661c7ca]{margin-top:13px}.customerOutLine_server_content_handle>div[data-v-6661c7ca]{height:40px;background:#3875ea;border-radius:3px;color:#fff;font-size:13px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.max_style[data-v-6661c7ca]{max-width:375px;max-height:668px}
.customerOutLine_server[data-v-3d0c53fc]{width:100%;height:100%;position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.customerOutLine_server_header[data-v-3d0c53fc]{height:50px;background:-webkit-gradient(linear,right top,left top,from(#1890ff),to(#3875ea));background:linear-gradient(270deg,#1890ff,#3875ea);color:#fff;font-size:16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:0 18px}.customerOutLine_server_header .pc_customerServer_container_header_handle[data-v-3d0c53fc]{cursor:pointer}.customerOutLine_server_content[data-v-3d0c53fc]{padding:17px 18px}.customerOutLine_server_content_message[data-v-3d0c53fc]{font-size:13px;color:#333;margin-bottom:26px}.customerOutLine_server_content_message_phone[data-v-3d0c53fc]{margin-top:30px}.customerOutLine_server_content_form>div[data-v-3d0c53fc]{margin-bottom:18px}.customerOutLine_server_content_form>div input[data-v-3d0c53fc]{border-radius:3px;border:1px solid #ececec;outline:none;font-size:13px;padding-left:14px;width:100%;height:40px}.customerOutLine_server_content_form>div textarea[data-v-3d0c53fc]{width:100%;border:1px solid #ececec;outline:none;font-size:13px;padding:14px}.customerOutLine_server_content_handle[data-v-3d0c53fc]{margin-top:13px}.customerOutLine_server_content_handle>div[data-v-3d0c53fc]{height:40px;background:#3875ea;border-radius:3px;color:#fff;font-size:13px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.max_style[data-v-3d0c53fc]{max-width:375px;max-height:668px}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
.customerOutLine_server[data-v-f92be0d8]{width:100%;height:100%;position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.customerOutLine_server_header[data-v-f92be0d8]{height:50px;background:-webkit-gradient(linear,right top,left top,from(#1890ff),to(#3875ea));background:linear-gradient(270deg,#1890ff,#3875ea);color:#fff;font-size:16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:0 18px}.customerOutLine_server_header .pc_customerServer_container_header_handle[data-v-f92be0d8]{cursor:pointer}.customerOutLine_server_content[data-v-f92be0d8]{padding:17px 18px}.customerOutLine_server_content .icon[data-v-f92be0d8]{width:70px;height:70px;margin:auto;background:#55d443;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;margin-top:34px}.customerOutLine_server_content .icon .iconfont[data-v-f92be0d8]{color:#fff;font-size:32px}.customerOutLine_server_content .title[data-v-f92be0d8]{text-align:center;margin:15px 0 6px 0;font-size:16px;font-weight:700}.customerOutLine_server_content .message[data-v-f92be0d8]{text-align:center}.customerOutLine_server_content .message span[data-v-f92be0d8]{display:inline-block;text-align:left}.customerOutLine_server_content .button[data-v-f92be0d8]{width:160px;height:40px;background:#3875ea;border-radius:3px;font-size:13px;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin:auto;margin-top:35px}
.customerOutLine_server[data-v-31063b0c]{width:100%;height:100%;position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.customerOutLine_server_header[data-v-31063b0c]{height:50px;background:-webkit-gradient(linear,right top,left top,from(#1890ff),to(#3875ea));background:linear-gradient(270deg,#1890ff,#3875ea);color:#fff;font-size:16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:0 18px}.customerOutLine_server_header .pc_customerServer_container_header_handle[data-v-31063b0c]{cursor:pointer}.customerOutLine_server_content[data-v-31063b0c]{padding:17px 18px}.customerOutLine_server_content .icon[data-v-31063b0c]{width:70px;height:70px;margin:auto;background:#55d443;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;margin-top:34px}.customerOutLine_server_content .icon .iconfont[data-v-31063b0c]{color:#fff;font-size:32px}.customerOutLine_server_content .title[data-v-31063b0c]{text-align:center;margin:15px 0 6px 0;font-size:16px;font-weight:700}.customerOutLine_server_content .message[data-v-31063b0c]{text-align:center}.customerOutLine_server_content .message span[data-v-31063b0c]{display:inline-block;text-align:left}.customerOutLine_server_content .button[data-v-31063b0c]{width:160px;height:40px;background:#3875ea;border-radius:3px;font-size:13px;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin:auto;margin-top:35px}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-0cc6334f"],{"151a":function(t,e,s){t.exports=s.p+"img/feed-icon.99e39a74.png"},5909:function(t,e,s){"use strict";s.r(e);s("7f7f");var a=function(){var t=this,e=t._self._c;return e("div",{staticClass:"feedback-wrapper"},[e("div",{staticClass:"head"},[e("div",{staticClass:"left-wrapper"},[e("div",{staticClass:"title"},[t._v("商城客服已离线")]),e("div",{staticClass:"txt"},[t._v(t._s(t.feedback))])]),t._m(0)]),e("div",{staticClass:"main"},[e("div",{staticClass:"title"},[t._v("我要反馈")]),e("div",{staticClass:"input-box"},[e("Input",{attrs:{type:"text",placeholder:"请输入您的姓名"},model:{value:t.name,callback:function(e){t.name=e},expression:"name"}})],1),e("div",{staticClass:"input-box"},[e("Input",{attrs:{type:"text",placeholder:"请输入您的联系电话"},model:{value:t.phone,callback:function(e){t.phone=e},expression:"phone"}})],1),e("div",{staticClass:"input-box"},[e("Input",{attrs:{type:"textarea",placeholder:"请填写内容"},model:{value:t.con,callback:function(e){t.con=e},expression:"con"}})],1),e("Button",{staticClass:"sub_btn",attrs:{disabled:t.isDisabled},on:{click:t.subMit}},[t._v("提交")])],1)])},n=[function(){var t=this,e=t._self._c;return e("div",{staticClass:"img-box"},[e("img",{attrs:{src:s("151a"),mode:""}})])}],i=s("42e3"),c={name:"feedback",data:function(){return{name:"",phone:"",con:"",feedback:"",isDisabled:!1}},created:function(){this.getInfo()},methods:{getInfo:function(){var t=this;Object(i["i"])().then((function(e){t.feedback=e.data.feedback}))},subMit:function(){var t=this;return this.name?this.phone&&/^1(3|4|5|7|8|9|6)\d{9}$/i.test(this.phone)?this.con?(this.isDisabled=!0,void Object(i["j"])({rela_name:this.name,phone:this.phone,content:this.con}).then((function(e){t.$Message.success(e.msg),t.$router.go(-1)})).catch((function(e){t.$Message.error(e.msg)}))):this.$Message.error("请填写内容"):this.$Message.error("请填写正确的手机号码"):this.$Message.error("请填写姓名")}}},o=c,r=(s("a433"),s("2877")),l=Object(r["a"])(o,a,n,!1,null,null,null);e["default"]=l.exports},a433:function(t,e,s){"use strict";s("cdb6")},cdb6:function(t,e,s){}}]);
@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-1038b9c2"],{"151a":function(t,e,s){t.exports=s.p+"img/feed-icon.99e39a74.png"},"1fd1":function(t,e,s){},5909:function(t,e,s){"use strict";s.r(e);s("7f7f");var a=function(){var t=this,e=t._self._c;return e("div",{staticClass:"feedback-wrapper"},[e("div",{staticClass:"head"},[e("div",{staticClass:"left-wrapper"},[e("div",{staticClass:"title"},[t._v("客服已离线")]),e("div",{staticClass:"txt"},[t._v(t._s(t.feedback))])]),t._m(0)]),e("div",{staticClass:"main"},[e("div",{staticClass:"title"},[t._v("我要反馈")]),e("div",{staticClass:"input-box"},[e("Input",{attrs:{type:"text",placeholder:"请输入您的姓名"},model:{value:t.name,callback:function(e){t.name=e},expression:"name"}})],1),e("div",{staticClass:"input-box"},[e("Input",{attrs:{type:"text",placeholder:"请输入您的联系电话"},model:{value:t.phone,callback:function(e){t.phone=e},expression:"phone"}})],1),e("div",{staticClass:"input-box"},[e("Input",{attrs:{type:"textarea",placeholder:"请填写内容"},model:{value:t.con,callback:function(e){t.con=e},expression:"con"}})],1),e("Button",{staticClass:"sub_btn",attrs:{disabled:t.isDisabled},on:{click:t.subMit}},[t._v("提交")])],1)])},n=[function(){var t=this,e=t._self._c;return e("div",{staticClass:"img-box"},[e("img",{attrs:{src:s("151a"),mode:""}})])}],i=s("42e3"),c={name:"feedback",data:function(){return{name:"",phone:"",con:"",feedback:"",isDisabled:!1}},created:function(){this.getInfo()},methods:{getInfo:function(){var t=this;Object(i["i"])().then((function(e){t.feedback=e.data.feedback}))},subMit:function(){var t=this;return this.name?this.phone&&/^1(3|4|5|7|8|9|6)\d{9}$/i.test(this.phone)?this.con?(this.isDisabled=!0,void Object(i["j"])({rela_name:this.name,phone:this.phone,content:this.con}).then((function(e){t.$Message.success(e.msg),t.$router.go(-1)})).catch((function(e){t.$Message.error(e.msg)}))):this.$Message.error("请填写内容"):this.$Message.error("请填写正确的手机号码"):this.$Message.error("请填写姓名")}}},o=c,r=(s("7140"),s("2877")),l=Object(r["a"])(o,a,n,!1,null,null,null);e["default"]=l.exports},7140:function(t,e,s){"use strict";s("1fd1")}}]);
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-15ca3ee6"],{"0b90":function(e,t,a){"use strict";a.r(t);var n=function(){var e=this,t=e._self._c;return t("div",{staticClass:"customerOutLine_server",class:{max_style:!e.isMobile}},[t("div",{staticClass:"customerOutLine_server_header"},[t("span",[e._v("客服已离线")]),t("div",{staticClass:"pc_customerServer_container_header_handle",on:{click:e.closeIframe}},[t("span",{staticClass:"iconfont"},[e._v("")])])]),t("div",{staticClass:"customerOutLine_server_content"},[t("div",{staticClass:"customerOutLine_server_content_message",domProps:{innerHTML:e._s(e.feedback)}},[t("div",[e._v("您好,现在客服不在线,请留言。如果没有留下您的联系方式,客服将无法和您联系!")]),e._m(0)]),t("div",{staticClass:"customerOutLine_server_content_form"},[t("div",[t("input",{directives:[{name:"model",rawName:"v-model",value:e.feedData.rela_name,expression:"feedData.rela_name"}],attrs:{type:"text",placeholder:"请输入您的姓名"},domProps:{value:e.feedData.rela_name},on:{input:function(t){t.target.composing||e.$set(e.feedData,"rela_name",t.target.value)}}})]),t("div",[t("input",{directives:[{name:"model",rawName:"v-model",value:e.feedData.phone,expression:"feedData.phone"}],attrs:{type:"number",placeholder:"请输入您的联系电话"},domProps:{value:e.feedData.phone},on:{input:function(t){t.target.composing||e.$set(e.feedData,"phone",t.target.value)}}})]),t("div",[t("textarea",{directives:[{name:"model",rawName:"v-model",value:e.feedData.content,expression:"feedData.content"}],attrs:{name:"",id:"",cols:"30",rows:"10",placeholder:"请填写留言内容"},domProps:{value:e.feedData.content},on:{input:function(t){t.target.composing||e.$set(e.feedData,"content",t.target.value)}}})])]),t("div",{staticClass:"customerOutLine_server_content_handle"},[t("div",{on:{click:e.postFeedMessage}},[t("span",[e._v("提交留言")])])])])])},s=[function(){var e=this,t=e._self._c;return t("div",{staticClass:"customerOutLine_server_content_message_phone"},[t("div",[e._v("\n 我们的工作时间:09:00~22:00\n ")]),t("div",[e._v("\n 售前客服电话:400-8888-794\n ")])])}],r=(a("8e6e"),a("ac6a"),a("456d"),a("ade3")),o=a("42e3"),c=a("2f62");function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function u(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?i(Object(a),!0).forEach((function(t){Object(r["a"])(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):i(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}var d={data:function(){return{feedback:"",feedData:{rela_name:"",phone:"",content:""}}},computed:u({},Object(c["e"])("media",["isMobile"])),created:function(){this.selectFeedBack(),parent.postMessage({type:"customerOutLine"},"*")},methods:{selectFeedBack:function(){var e=this;Object(o["M"])().then((function(t){200==t.status&&(e.feedback=t.data.feedback)}))},postFeedMessage:function(){var e=this;Object(o["N"])(this.feedData).then((function(t){200==t.status&&(e.$Message.success("提交成功"),e.$router.push({name:"finishSubmitOutLine",query:e.$route.query}))})).catch((function(t){e.$Message.error(t.msg)}))},closeIframe:function(){parent.postMessage({type:"closeWindow"},"*")}}},l=d,p=(a("78d7"),a("2877")),f=Object(p["a"])(l,n,s,!1,null,"3d0c53fc",null);t["default"]=f.exports},"78d7":function(e,t,a){"use strict";a("fa87")},fa87:function(e,t,a){}}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-406f5202"],{"3fdb":function(e,t,s){},7647:function(e,t,s){"use strict";s.r(t);var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"customerOutLine_server",class:{max_style:!e.isMobile}},[t("div",{staticClass:"customerOutLine_server_header"},[t("span",[e._v("客服已离线")]),t("div",{staticClass:"pc_customerServer_container_header_handle",on:{click:e.closeIframe}},[t("span",{staticClass:"iconfont"},[e._v("")])])]),t("div",{staticClass:"customerOutLine_server_content"},[e._m(0),e._m(1),e._m(2),t("div",{staticClass:"button",on:{click:e.closeIframe}},[t("span",[e._v("好的")])])])])},n=[function(){var e=this,t=e._self._c;return t("div",{staticClass:"icon"},[t("span",{staticClass:"iconfont"},[e._v("")])])},function(){var e=this,t=e._self._c;return t("div",{staticClass:"title"},[t("span",[e._v("提交成功")])])},function(){var e=this,t=e._self._c;return t("div",{staticClass:"message"},[t("span",[e._v("您的信息已提交成功,我们会尽快与您取得联系!")])])}],c=(s("8e6e"),s("ac6a"),s("456d"),s("ade3")),a=(s("42e3"),s("2f62"));function i(e,t){var s=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),s.push.apply(s,r)}return s}function o(e){for(var t=1;t<arguments.length;t++){var s=null!=arguments[t]?arguments[t]:{};t%2?i(Object(s),!0).forEach((function(t){Object(c["a"])(e,t,s[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(s)):i(Object(s)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(s,t))}))}return e}var u={data:function(){return{feedback:"",feedData:{rela_name:"",phone:"",content:""}}},computed:o({},Object(a["e"])("media",["isMobile"])),created:function(){},methods:{closeIframe:function(){parent.postMessage({type:"closeWindow"},"*"),this.$router.push({name:"customerServerRedirect",query:this.$route.query}),parent.postMessage({type:"reload"},"*")}}},l=u,f=(s("ad1d"),s("2877")),p=Object(f["a"])(l,r,n,!1,null,"31063b0c",null);t["default"]=p.exports},ad1d:function(e,t,s){"use strict";s("3fdb")}}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-6eca2b4f"],{"0b90":function(e,t,a){"use strict";a.r(t);var n=function(){var e=this,t=e._self._c;return t("div",{staticClass:"customerOutLine_server",class:{max_style:!e.isMobile}},[t("div",{staticClass:"customerOutLine_server_header"},[t("span",[e._v("商城客服已离线")]),t("div",{staticClass:"pc_customerServer_container_header_handle",on:{click:e.closeIframe}},[t("span",{staticClass:"iconfont"},[e._v("")])])]),t("div",{staticClass:"customerOutLine_server_content"},[t("div",{staticClass:"customerOutLine_server_content_message",domProps:{innerHTML:e._s(e.feedback)}},[t("div",[e._v("您好,现在客服不在线,请留言。如果没有留下您的联系方式,客服将无法和您联系!")]),e._m(0)]),t("div",{staticClass:"customerOutLine_server_content_form"},[t("div",[t("input",{directives:[{name:"model",rawName:"v-model",value:e.feedData.rela_name,expression:"feedData.rela_name"}],attrs:{type:"text",placeholder:"请输入您的姓名"},domProps:{value:e.feedData.rela_name},on:{input:function(t){t.target.composing||e.$set(e.feedData,"rela_name",t.target.value)}}})]),t("div",[t("input",{directives:[{name:"model",rawName:"v-model",value:e.feedData.phone,expression:"feedData.phone"}],attrs:{type:"number",placeholder:"请输入您的联系电话"},domProps:{value:e.feedData.phone},on:{input:function(t){t.target.composing||e.$set(e.feedData,"phone",t.target.value)}}})]),t("div",[t("textarea",{directives:[{name:"model",rawName:"v-model",value:e.feedData.content,expression:"feedData.content"}],attrs:{name:"",id:"",cols:"30",rows:"10",placeholder:"请填写留言内容"},domProps:{value:e.feedData.content},on:{input:function(t){t.target.composing||e.$set(e.feedData,"content",t.target.value)}}})])]),t("div",{staticClass:"customerOutLine_server_content_handle"},[t("div",{on:{click:e.postFeedMessage}},[t("span",[e._v("提交留言")])])])])])},s=[function(){var e=this,t=e._self._c;return t("div",{staticClass:"customerOutLine_server_content_message_phone"},[t("div",[e._v("\n 我们的工作时间:09:00~22:00\n ")]),t("div",[e._v("\n 售前客服电话:400-8888-794\n ")])])}],r=(a("8e6e"),a("ac6a"),a("456d"),a("ade3")),o=a("42e3"),c=a("2f62");function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function u(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?i(Object(a),!0).forEach((function(t){Object(r["a"])(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):i(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}var d={data:function(){return{feedback:"",feedData:{rela_name:"",phone:"",content:""}}},computed:u({},Object(c["e"])("media",["isMobile"])),created:function(){this.selectFeedBack(),parent.postMessage({type:"customerOutLine"},"*")},methods:{selectFeedBack:function(){var e=this;Object(o["M"])().then((function(t){200==t.status&&(e.feedback=t.data.feedback)}))},postFeedMessage:function(){var e=this;Object(o["N"])(this.feedData).then((function(t){200==t.status&&(e.$Message.success("提交成功"),e.$router.push({name:"finishSubmitOutLine",query:e.$route.query}))})).catch((function(t){e.$Message.error(t.msg)}))},closeIframe:function(){parent.postMessage({type:"closeWindow"},"*")}}},l=d,p=(a("dd7b"),a("2877")),f=Object(p["a"])(l,n,s,!1,null,"6661c7ca",null);t["default"]=f.exports},"0f37":function(e,t,a){},dd7b:function(e,t,a){"use strict";a("0f37")}}]);
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7ee3add7"],{"0db9":function(e,t,s){"use strict";s("1bf0")},"1bf0":function(e,t,s){},7647:function(e,t,s){"use strict";s.r(t);var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"customerOutLine_server",class:{max_style:!e.isMobile}},[t("div",{staticClass:"customerOutLine_server_header"},[t("span",[e._v("商城客服已离线")]),t("div",{staticClass:"pc_customerServer_container_header_handle",on:{click:e.closeIframe}},[t("span",{staticClass:"iconfont"},[e._v("")])])]),t("div",{staticClass:"customerOutLine_server_content"},[e._m(0),e._m(1),e._m(2),t("div",{staticClass:"button",on:{click:e.closeIframe}},[t("span",[e._v("好的")])])])])},n=[function(){var e=this,t=e._self._c;return t("div",{staticClass:"icon"},[t("span",{staticClass:"iconfont"},[e._v("")])])},function(){var e=this,t=e._self._c;return t("div",{staticClass:"title"},[t("span",[e._v("提交成功")])])},function(){var e=this,t=e._self._c;return t("div",{staticClass:"message"},[t("span",[e._v("您的信息已提交成功,我们会尽快与您取得联系!")])])}],c=(s("8e6e"),s("ac6a"),s("456d"),s("ade3")),a=(s("42e3"),s("2f62"));function i(e,t){var s=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),s.push.apply(s,r)}return s}function o(e){for(var t=1;t<arguments.length;t++){var s=null!=arguments[t]?arguments[t]:{};t%2?i(Object(s),!0).forEach((function(t){Object(c["a"])(e,t,s[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(s)):i(Object(s)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(s,t))}))}return e}var u={data:function(){return{feedback:"",feedData:{rela_name:"",phone:"",content:""}}},computed:o({},Object(a["e"])("media",["isMobile"])),created:function(){},methods:{closeIframe:function(){parent.postMessage({type:"closeWindow"},"*"),this.$router.push({name:"customerServerRedirect",query:this.$route.query}),parent.postMessage({type:"reload"},"*")}}},l=u,f=(s("0db9"),s("2877")),p=Object(f["a"])(l,r,n,!1,null,"f92be0d8",null);t["default"]=p.exports}}]);
File diff suppressed because one or more lines are too long
+3
View File
@@ -6,6 +6,9 @@ FROM ${COMPOSER_IMAGE} AS composer
FROM ${PHP_BASE_IMAGE}
RUN set -eux; \
# bullseye 已 EOL(2026-08),security 包在镜像站/archive 间迁移暂不可用:构建期去掉 security 源,
# 仅用 main/updates(阿里云池完好)。中期应将基础镜像升级到受支持的 Debian/PHP 版本
sed -i '/debian-security/d; s|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list; \
apt-get update && apt-get install -y --no-install-recommends \
libfreetype6-dev \
libjpeg62-turbo-dev \
+10 -1
View File
@@ -40,9 +40,18 @@ done
echo " MySQL is ready!"
echo "==> Importing database schema..."
# 仅在空库(无任何业务表)时导入初始化 SQL,避免容器重启/线上更新代码时重置数据库。
# 如需重置数据:docker compose down -v 清掉数据卷后重新启动即可触发导入。
# 以 eb_system_admin(后台账号表,初始化 SQL 中较靠后)作为初始化完成的哨兵:部分导入的库不会被误判为已初始化
TABLE_SENTINEL=$(mysql -h "${DB_HOSTNAME:-mysql}" -u "${DB_USERNAME:-root}" -p"${DB_PASSWORD:-root}" -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${DB_DATABASE:-crmeb_chat}' AND table_name='eb_system_admin';" 2>/dev/null || echo 0)
if [ "${TABLE_SENTINEL:-0}" != "0" ]; then
echo " Database already initialized (sentinel table found), skipping import to protect existing data."
else
echo " Empty database detected, importing crmeb_clean.sql..."
mysql -h "${DB_HOSTNAME:-mysql}" -u "${DB_USERNAME:-root}" -p"${DB_PASSWORD:-root}" --default-character-set=utf8mb4 "${DB_DATABASE:-crmeb_chat}" < /var/www/html/public/install/crmeb_clean.sql 2>/dev/null && \
echo " Database schema imported." || \
echo " Database may already be initialized (ignoring errors)."
echo " Database import failed."
fi
echo "==> Skipping install wizard..."
touch /var/www/html/public/install/install.lock
+3
View File
@@ -6,6 +6,9 @@ server {
charset utf-8;
# 容器内监听 80、对外映射 18091:绝对重定向会丢外部端口,改发相对 Location
absolute_redirect off;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
+58
View File
@@ -25,8 +25,66 @@ cp .env.docker .env
docker compose up -d
```
## 主服务器部署与更新(8.133.254.174)
生产环境部署在主服务器阿里云,IP 直连 18091 端口(无域名、无证书,勿再使用已废弃的 kefu.wildcs2.com)。
### 服务器访问
```bash
ssh -i "C:/data/密钥/祝总阿里云.pem" root@8.133.254.174
```
- 部署目录:`/www/projects/crmchat`(gitea clone,master 分支)
- 访问地址:`http://8.133.254.174:18091`(阿里云安全组已放行 18091/TCP)
- 代码推送:`git push origin master`(偶发认证失败,重试一次即可)
### 更新部署标准流程(顺序不可颠倒)
```bash
# 本地:提交并推送
git push origin master
# 服务器:拉取 → 重建 php 镜像 → 重建容器 → 重启 nginx → 健康检查
cd /www/projects/crmchat
git pull
docker compose build php
docker compose up -d php
docker restart crmchat-nginx
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:18091/chat/index # 预期 200
```
每一步都有原因,跳过或换序会出事故:
| 步骤 | 为什么 |
|------|--------|
| `docker compose build php` 必须在任何重启之前 | 旧镜像的 entrypoint 没有空库守卫,**对旧镜像容器执行 restart 会无条件重导初始化 SQL,清空整个数据库**(2026-09-25 已踩:清掉了用户会话数据)。新镜像的 entrypoint 检测到库已初始化(`eb_system_admin` 哨兵表存在)会跳过导入 |
| `up -d php` 而非 `restart php` | 新 entrypoint 在新镜像里,restart 不换镜像;up -d 重建容器才会用新镜像 |
| `docker restart crmchat-nginx` 收尾 | ① php 容器重建后 IP 变化,nginx 缓存旧 IP → 502;② `nginx.conf` 是单文件 bind mount,git pull 替换文件后容器仍指向旧 inode,`nginx -s reload` 读到的还是旧配置,必须 restart 容器重新挂载 |
### 已固化的构建/配置修改(勿回退)
- `docker/php/Dockerfile`:apt 源切阿里云镜像。原因:`php:7.4-cli` 基于 Debian bullseye,LTS 已于 2026-08-31 结束,deb.debian.org 从 ECS 访问超时、各镜像站陆续清理 bullseye 包(404);security 源在镜像站/archive 迁移空窗期同样 404,暂去掉(基础镜像本身是 2022 快照,无实际回退;中期应升级基础镜像到受支持的 Debian/PHP 版本)
- `nginx/nginx.conf`:`absolute_redirect off`。原因:容器内 nginx 监听 80、对外映射 18091,nginx 发绝对 Location 重定向时用的是容器内端口(80),外部端口 18091 会丢失
- `entrypoint.sh`:空库守卫 + `eb_system_admin` 哨兵表检查(见上)
### 忘记密码重置方法
```bash
# 在服务器上执行(以 admin 为例,kefu 同理换表名字段)
cd /www/projects/crmchat
MYSQLPASS=$(grep '^MYSQL_ROOT_PASSWORD=' .env | cut -d= -f2)
NEWPASS=$(openssl rand -hex 8); echo "新密码: $NEWPASS"
HASH=$(docker exec crmchat-php php -r "echo password_hash(\$argv[1], PASSWORD_DEFAULT);" "$NEWPASS")
docker exec crmchat-mysql mysql -uroot -p"$MYSQLPASS" crmeb_chat \
-e "UPDATE eb_system_admin SET pwd='$HASH' WHERE account='admin';"
# kefu 坐席:UPDATE eb_chat_service SET password='$HASH' WHERE account='kefu';
```
## 访问地址
> 本地开发默认 admin123 / kefu123;**主服务器(8.133.254.174:18091)的 admin / kefu 密码已于 2026-09-25 重置为随机值**(不再使用默认密码),重置方法见「主服务器部署与更新」。
| 入口 | 地址 | 账号 | 密码 |
|------|------|------|------|
| 后台管理 | `http://localhost:18091/admin` | admin | admin123 |