Compare commits
5 Commits
73b5efeb8f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 09a8300064 | |||
| e1a93c1496 | |||
| bc5b5bf8ff | |||
| babd3b5aa0 | |||
| 975ec6758b |
@@ -0,0 +1,17 @@
|
|||||||
|
# 2026-08-11 工作记录
|
||||||
|
|
||||||
|
## 日志诊断:cms-api (websoft-modules) 报错
|
||||||
|
|
||||||
|
来源文件:/Users/gxwebsoft/Downloads/websoft-modules (1).log
|
||||||
|
|
||||||
|
### 主报错(反复出现,需修复)
|
||||||
|
- 现象:`CmsWebsiteController.update()` → `updateById(cmsWebsite)` 生成非法 SQL
|
||||||
|
`UPDATE cms_website WHERE website_id=? AND deleted=0`(SET 子句为空)
|
||||||
|
- 抛错链:MyBatis-Plus `TenantLineInnerInterceptor` 用 jsqlparser 解析该空 SET SQL → `ParseException: Encountered unexpected token: "WHERE"` → `MybatisPlusException: Failed to process`
|
||||||
|
- 根因:`application.yml` 未设置全局 `field-strategy`,MP 3.4.x 默认 `NOT_NULL`。当 PUT /cms/website 的 body 只有 websiteId(websiteCode 被置 null,其余字段全 null)时,`updateById` 没有任何非空字段可写 → SET 为空 → SQL 非法。
|
||||||
|
- 实体佐证:`CmsWebsite` 的 `@TableLogic deleted` 进 WHERE、`@TableId websiteId` 进 WHERE、`updateTime` 无 `@TableField(fill)` 自动填充 → 无字段兜底。
|
||||||
|
- 已修复(2026-08-11):在 `CmsWebsiteController.update()` 调用 `updateById` 前,用 `BeanUtil.beanToMap` 判断是否存在非 id/deleted 的非空字段;为空则直接 `success` 跳过 DB 更新。已新增 import `cn.hutool.core.bean.BeanUtil`。
|
||||||
|
|
||||||
|
### 次要报错(非紧急)
|
||||||
|
- 01:44:17 MQTT:client `hjm_car_1786362857263` keepAlive=20s 超时,`MQTT连接丢失`(GPS 回调)。Paho 通常会自动重连,疑为 broker 网络/keepalive 问题。
|
||||||
|
- 07:35:20 WARN Druid:`removeAbandoned is true, not use in production.` 连接池 abandoned 清理开启,Druid 不建议生产环境开启,属配置告警。
|
||||||
@@ -2,6 +2,7 @@ package com.gxwebsoft.cms.controller;
|
|||||||
|
|
||||||
import cn.hutool.core.date.DateTime;
|
import cn.hutool.core.date.DateTime;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
@@ -22,6 +23,7 @@ import com.gxwebsoft.common.system.entity.User;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -52,6 +54,9 @@ public class CmsWebsiteController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private CmsWebsiteSettingService cmsWebsiteSettingService;
|
private CmsWebsiteSettingService cmsWebsiteSettingService;
|
||||||
|
|
||||||
|
@Value("${internal.key:websopy-internal-2025}")
|
||||||
|
private String internalKey;
|
||||||
|
|
||||||
private static final String SITE_INFO_KEY_PREFIX = "SiteInfo:";
|
private static final String SITE_INFO_KEY_PREFIX = "SiteInfo:";
|
||||||
private static final String MP_INFO_KEY_PREFIX = "MpInfo:";
|
private static final String MP_INFO_KEY_PREFIX = "MpInfo:";
|
||||||
private static final String SELECT_PAYMENT_KEY_PREFIX = "SelectPayment:";
|
private static final String SELECT_PAYMENT_KEY_PREFIX = "SelectPayment:";
|
||||||
@@ -147,6 +152,15 @@ public class CmsWebsiteController extends BaseController {
|
|||||||
// 不允许通过此接口清空或修改 websiteCode(设为 null 则 MP 不更新该字段)
|
// 不允许通过此接口清空或修改 websiteCode(设为 null 则 MP 不更新该字段)
|
||||||
cmsWebsite.setWebsiteCode(null);
|
cmsWebsite.setWebsiteCode(null);
|
||||||
}
|
}
|
||||||
|
// 修复:当实体没有任何非空(可更新)字段时,MyBatis-Plus 默认 NOT_NULL 策略会生成
|
||||||
|
// 缺 SET 子句的非法 SQL(如 "UPDATE cms_website WHERE ..."),触发多租户插件 jsqlparser
|
||||||
|
// 解析异常。先判断是否存在 id / deleted 之外的非空字段,空更新视为幂等成功。
|
||||||
|
boolean hasUpdatable = BeanUtil.beanToMap(cmsWebsite, false, false).entrySet().stream()
|
||||||
|
.anyMatch(e -> !"websiteId".equals(e.getKey()) && !"deleted".equals(e.getKey())
|
||||||
|
&& e.getValue() != null);
|
||||||
|
if (!hasUpdatable) {
|
||||||
|
return success("修改成功");
|
||||||
|
}
|
||||||
if (cmsWebsiteService.updateById(cmsWebsite)) {
|
if (cmsWebsiteService.updateById(cmsWebsite)) {
|
||||||
// 站点信息(含 domain / templateId 等)有 1 天 Redis 缓存,改完必须立刻失效,
|
// 站点信息(含 domain / templateId 等)有 1 天 Redis 缓存,改完必须立刻失效,
|
||||||
// 否则公开站与后台链接拼接最长 1 天读到旧值(与 CmsNavigationController 写操作保持一致)。
|
// 否则公开站与后台链接拼接最长 1 天读到旧值(与 CmsNavigationController 写操作保持一致)。
|
||||||
@@ -679,5 +693,47 @@ public class CmsWebsiteController extends BaseController {
|
|||||||
return success(cmsWebsiteService.pageReviews(param));
|
return success(cmsWebsiteService.pageReviews(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部接口:由 websopy 支付回调在服务端调用,将站点置为「运行中(1)」。
|
||||||
|
* 通过 X-Internal-Key 头做简单鉴权(仅限内网/可信服务调用,密钥与 websopy 端保持一致)。
|
||||||
|
*/
|
||||||
|
@PostMapping("/internal/activate")
|
||||||
|
public ApiResult<?> activateInternal(
|
||||||
|
@RequestHeader(value = "X-Internal-Key", required = false) String key,
|
||||||
|
@RequestBody Map<String, Object> body) {
|
||||||
|
if (key == null || !key.equals(internalKey)) {
|
||||||
|
return fail("非法请求");
|
||||||
|
}
|
||||||
|
Integer websiteId = body.get("websiteId") instanceof Number
|
||||||
|
? ((Number) body.get("websiteId")).intValue() : null;
|
||||||
|
if (websiteId == null) {
|
||||||
|
return fail("websiteId 不能为空");
|
||||||
|
}
|
||||||
|
Integer status = body.get("status") instanceof Number
|
||||||
|
? ((Number) body.get("status")).intValue() : 1;
|
||||||
|
Integer running = body.get("running") instanceof Number
|
||||||
|
? ((Number) body.get("running")).intValue() : 0;
|
||||||
|
|
||||||
|
CmsWebsite site = new CmsWebsite();
|
||||||
|
site.setWebsiteId(websiteId);
|
||||||
|
site.setStatus(status);
|
||||||
|
site.setRunning(running);
|
||||||
|
if (cmsWebsiteService.updateById(site)) {
|
||||||
|
// 失效站点缓存,保证后台/公开站尽快读到新状态
|
||||||
|
try {
|
||||||
|
CmsWebsite exist = cmsWebsiteService.getOne(new LambdaQueryWrapper<CmsWebsite>()
|
||||||
|
.eq(CmsWebsite::getWebsiteId, websiteId).last("limit 1"));
|
||||||
|
if (exist != null && ObjectUtil.isNotEmpty(exist.getTenantId())) {
|
||||||
|
redisUtil.delete(SITE_INFO_KEY_PREFIX.concat(exist.getTenantId().toString()));
|
||||||
|
redisUtil.delete(MP_INFO_KEY_PREFIX.concat(exist.getTenantId().toString()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("清除站点缓存失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return success("站点已激活");
|
||||||
|
}
|
||||||
|
return fail("激活失败");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class CmsWebsite implements Serializable {
|
|||||||
@Schema(description = "自定义API接口")
|
@Schema(description = "自定义API接口")
|
||||||
private String apiUrl;
|
private String apiUrl;
|
||||||
|
|
||||||
@Schema(description = "应用版本 10免费版 20授权版 30永久授权")
|
@Schema(description = "应用版本 0体验版 10基础版 20授权版 30永久授权")
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|
||||||
@Schema(description = "服务到期时间")
|
@Schema(description = "服务到期时间")
|
||||||
@@ -117,6 +117,13 @@ public class CmsWebsite implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String templateName;
|
private String templateName;
|
||||||
|
|
||||||
|
@Schema(description = "克隆来源租户UID(建站时据此复制参考租户内容;值为 sys_user.template_id,后端据此按模板分类)")
|
||||||
|
private Integer cloneTenantId;
|
||||||
|
|
||||||
|
@Schema(description = "是否初始化示例数据(仅 create 入参使用,不落库;true=生成标准示例栏目与内容,false=空白骨架,null=沿用模板克隆历史行为)")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Boolean initDemoData;
|
||||||
|
|
||||||
@Schema(description = "行业类型(父级)")
|
@Schema(description = "行业类型(父级)")
|
||||||
private String industryParent;
|
private String industryParent;
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,9 @@
|
|||||||
<if test="param.templateId != null">
|
<if test="param.templateId != null">
|
||||||
template_id = #{param.templateId},
|
template_id = #{param.templateId},
|
||||||
</if>
|
</if>
|
||||||
|
<if test="param.cloneTenantId != null">
|
||||||
|
clone_tenant_id = #{param.cloneTenantId},
|
||||||
|
</if>
|
||||||
<if test="param.companyId != null">
|
<if test="param.companyId != null">
|
||||||
company_id = #{param.companyId},
|
company_id = #{param.companyId},
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ import com.gxwebsoft.shop.vo.ShopVo;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -69,6 +71,12 @@ public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebs
|
|||||||
@Resource
|
@Resource
|
||||||
private CmsArticleContentService cmsArticleContentService;
|
private CmsArticleContentService cmsArticleContentService;
|
||||||
@Resource
|
@Resource
|
||||||
|
private CmsProductService cmsProductService;
|
||||||
|
@Resource
|
||||||
|
private CmsCaseService cmsCaseService;
|
||||||
|
@Resource
|
||||||
|
private CmsPageService cmsPageService;
|
||||||
|
@Resource
|
||||||
private CmsWebsiteMapper cmsWebsiteMapper;
|
private CmsWebsiteMapper cmsWebsiteMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectService projectService;
|
private ProjectService projectService;
|
||||||
@@ -191,7 +199,9 @@ public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebs
|
|||||||
website.setUserId(loginUser.getUserId());
|
website.setUserId(loginUser.getUserId());
|
||||||
website.setDeveloper(loginUser.getNickname());
|
website.setDeveloper(loginUser.getNickname());
|
||||||
website.setTenantId(loginUser.getTenantId());
|
website.setTenantId(loginUser.getTenantId());
|
||||||
website.setTemplateId(loginUser.getTemplateId());
|
// 克隆来源租户UID = sys_user.template_id(注册时选定的模板租户),写入独立字段 cloneTenantId,
|
||||||
|
// 供后端按模板分类;同时作为内容克隆来源。template_id 留给 /templates 模板选择器写入。
|
||||||
|
website.setCloneTenantId(loginUser.getTemplateId());
|
||||||
website.setCompanyId(loginUser.getCompanyId());
|
website.setCompanyId(loginUser.getCompanyId());
|
||||||
|
|
||||||
// 默认运行中:status(后台枚举)与 running(getSiteInfo 状态展示用)保持一致,
|
// 默认运行中:status(后台枚举)与 running(getSiteInfo 状态展示用)保持一致,
|
||||||
@@ -227,7 +237,8 @@ public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebs
|
|||||||
// cmsWebsiteSettingService.save(setting);
|
// cmsWebsiteSettingService.save(setting);
|
||||||
|
|
||||||
// 将网站创建者的userId做为查询条件 10257(4716),10324(6978),10398(26564)
|
// 将网站创建者的userId做为查询条件 10257(4716),10324(6978),10398(26564)
|
||||||
Integer websiteUserId = website.getTemplateId();
|
// 克隆来源 = cloneTenantId(=sys_user.template_id),据此复制参考租户的内容
|
||||||
|
Integer websiteUserId = website.getCloneTenantId();
|
||||||
|
|
||||||
// 只有当templateId存在时才执行复制操作
|
// 只有当templateId存在时才执行复制操作
|
||||||
if (websiteUserId != null && websiteUserId > 0) {
|
if (websiteUserId != null && websiteUserId > 0) {
|
||||||
@@ -279,61 +290,17 @@ public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebs
|
|||||||
});
|
});
|
||||||
cmsLinkService.saveBatch(links);
|
cmsLinkService.saveBatch(links);
|
||||||
|
|
||||||
// TODO 复制栏目和文章、文章内容
|
// 栏目与内容初始化:按 initDemoData 决定分支
|
||||||
CmsNavigationParam cmsNavigationParam = new CmsNavigationParam();
|
if (Boolean.TRUE.equals(website.getInitDemoData())) {
|
||||||
cmsNavigationParam.setWebsiteUserId(websiteUserId);
|
// 注册流程勾选「填充示例内容」:生成标准示例栏目与内容(不克隆模板,避免千篇一律)
|
||||||
cmsNavigationParam.setParentId(0);
|
initDemoData(website, loginUser);
|
||||||
final List<CmsNavigation> parents = cmsNavigationMapper.selectListAllRel(cmsNavigationParam);
|
} else if (Boolean.FALSE.equals(website.getInitDemoData())) {
|
||||||
parents.forEach(d -> {
|
// 注册流程明确不勾选:仅保留基础配置克隆,跳过栏目与文章,站点保持空白骨架
|
||||||
Integer navigationId = d.getNavigationId();
|
log.info("initDemoData=false,跳过示例内容生成,站点保持空白骨架, tenantId={}", loginUser.getTenantId());
|
||||||
// 复制顶级栏目
|
} else {
|
||||||
d.setTenantId(loginUser.getTenantId());
|
// 历史行为(后台手动建站等未传 initDemoData):克隆模板栏目与文章
|
||||||
d.setUserId(loginUser.getUserId());
|
cloneTemplateContent(website, loginUser, websiteUserId);
|
||||||
if (cmsNavigationService.save(d)) {
|
}
|
||||||
cmsNavigationService.saveAsync(d);
|
|
||||||
// 复制栏目文章
|
|
||||||
CmsArticleParam cmsArticleParam = new CmsArticleParam();
|
|
||||||
cmsArticleParam.setWebsiteUserId(websiteUserId);
|
|
||||||
cmsArticleParam.setCategoryId(navigationId);
|
|
||||||
final List<CmsArticle> articles = cmsArticleMapper.selectListAllRel(cmsArticleParam);
|
|
||||||
articles.forEach(a -> {
|
|
||||||
a.setCategoryId(d.getNavigationId());
|
|
||||||
a.setUserId(loginUser.getUserId());
|
|
||||||
a.setTenantId(loginUser.getTenantId());
|
|
||||||
if (cmsArticleService.save(a)) {
|
|
||||||
final CmsArticleContent content = new CmsArticleContent();
|
|
||||||
content.setArticleId(a.getArticleId());
|
|
||||||
content.setContent(a.getContent());
|
|
||||||
cmsArticleContentService.save(content);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 复制子栏目
|
|
||||||
cmsNavigationParam.setParentId(navigationId);
|
|
||||||
final List<CmsNavigation> navigations = cmsNavigationMapper.selectListAllRel(cmsNavigationParam);
|
|
||||||
navigations.forEach(c -> {
|
|
||||||
cmsArticleParam.setCategoryId(c.getNavigationId());
|
|
||||||
c.setParentId(d.getNavigationId());
|
|
||||||
c.setTenantId(loginUser.getTenantId());
|
|
||||||
c.setUserId(loginUser.getUserId());
|
|
||||||
cmsNavigationService.save(c);
|
|
||||||
cmsNavigationService.saveAsync(c);
|
|
||||||
// 复制子栏目文章
|
|
||||||
final List<CmsArticle> articles2 = cmsArticleMapper.selectListAllRel(cmsArticleParam);
|
|
||||||
articles2.forEach(a2 -> {
|
|
||||||
a2.setCategoryId(c.getNavigationId());
|
|
||||||
a2.setParentId(c.getParentId());
|
|
||||||
a2.setUserId(loginUser.getUserId());
|
|
||||||
a2.setTenantId(loginUser.getTenantId());
|
|
||||||
if (cmsArticleService.save(a2)) {
|
|
||||||
final CmsArticleContent content = new CmsArticleContent();
|
|
||||||
content.setArticleId(a2.getArticleId());
|
|
||||||
content.setContent(a2.getContent());
|
|
||||||
cmsArticleContentService.save(content);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
log.warn("没有有效的模板ID,跳过复制操作");
|
log.warn("没有有效的模板ID,跳过复制操作");
|
||||||
}
|
}
|
||||||
@@ -608,4 +575,218 @@ public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebs
|
|||||||
}
|
}
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 注册「填充示例内容」:标准示例数据初始化 =====
|
||||||
|
|
||||||
|
/** 产品示例数据:{ 产品名称, 简介 } */
|
||||||
|
private static final String[][] PRODUCT_DEMO = {
|
||||||
|
{"智能营销云平台", "一站式营销自动化工具,覆盖获客、培育、转化全链路,让增长可度量。"},
|
||||||
|
{"企业协同办公系统", "打通沟通、文档与流程,让团队协作更高效、信息不丢失。"},
|
||||||
|
{"数据可视化大屏", "多源数据接入,实时呈现业务全景,辅助经营决策。"},
|
||||||
|
{"客户关系管理 CRM", "沉淀客户资产,提升销售转化与复购,驱动业绩增长。"},
|
||||||
|
{"在线教育解决方案", "直播、录播、互动作业一体化的在线教学平台。"},
|
||||||
|
{"电商直播中台", "从商品到直播到交易,构建私域增长闭环。"}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 客户案例示例数据:{ 案例名称, 简介, 客户名称, 所属行业 } */
|
||||||
|
private static final String[][] CASE_DEMO = {
|
||||||
|
{"某制造业集团数字化转型", "以数字化中台打通生产与供应链,交付周期显著缩短。", "制造业集团", "制造业"},
|
||||||
|
{"连锁零售智慧门店升级", "门店数字化运营,会员复购率与坪效双提升。", "连锁零售企业", "零售"},
|
||||||
|
{"教育机构在线课堂改造", "线上线下融合教学,覆盖更多学员,扩大营收半径。", "教育科技公司", "教育"},
|
||||||
|
{"医疗机构患者服务中台", "统一预约、问诊与随访,患者满意度明显提升。", "三甲医院", "医疗"},
|
||||||
|
{"物流企业运输调度系统", "智能调度与轨迹监控,运力利用率大幅提高。", "物流企业", "物流"},
|
||||||
|
{"金融企业数据中台建设", "统一数据资产,风控与营销决策更加敏捷。", "城商银行", "金融"}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 新闻资讯示例数据:{ 标题, 摘要 }(标题支持 {company} 占位符,替换为注册企业名) */
|
||||||
|
private static final String[][] NEWS_DEMO = {
|
||||||
|
{"{company} 荣获年度数字化服务创新奖", "{company}凭借在企业官网与数字化服务领域的持续创新,荣获年度数字化服务创新奖。"},
|
||||||
|
{"企业官网建设的核心要素解析", "从品牌定位、信息架构到视觉体验,拆解一个高转化企业官网的关键要素。"},
|
||||||
|
{"如何选择适合自己的官网模板", "结合行业、预算与运营目标,教你快速匹配最合适的官网模板。"},
|
||||||
|
{"提升网站转化率的 5 个技巧", "落地页、CTA、信任背书、加载速度与内容策略,五招提升转化。"},
|
||||||
|
{"SEO 优化入门指南", "关键词、结构、内容与外链,新手也能上手的 SEO 基础框架。"},
|
||||||
|
{"品牌视觉设计与用户体验", "一致的视觉语言与顺畅的交互,是建立品牌信任的第一步。"}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成标准示例栏目与内容(注册流程勾选「填充示例内容」时调用)。
|
||||||
|
* 创建 5 个顶部导航栏目 + 关于我们单页 + 6 产品 + 6 案例 + 6 新闻。
|
||||||
|
* 不克隆模板内容,避免新站点千篇一律。
|
||||||
|
*/
|
||||||
|
private void initDemoData(CmsWebsite website, User loginUser) {
|
||||||
|
Integer tenantId = loginUser.getTenantId();
|
||||||
|
Integer userId = loginUser.getUserId();
|
||||||
|
final String companyName = StrUtil.isNotBlank(website.getWebsiteName()) ? website.getWebsiteName() : "贵公司";
|
||||||
|
|
||||||
|
// 1. 导航栏目:首页 / 关于我们 / 产品展示 / 客户案例 / 新闻资讯
|
||||||
|
CmsNavigation home = buildNav("首页", "page", "/", 1, 0, tenantId, userId);
|
||||||
|
CmsNavigation about = buildNav("关于我们", "page", "/about", 2, 1, tenantId, userId);
|
||||||
|
CmsNavigation productNav = buildNav("产品展示", "product", "/product", 3, 0, tenantId, userId);
|
||||||
|
CmsNavigation caseNav = buildNav("客户案例", "case", "/case", 4, 0, tenantId, userId);
|
||||||
|
CmsNavigation newsNav = buildNav("新闻资讯", "article", "/article", 5, 0, tenantId, userId);
|
||||||
|
for (CmsNavigation nav : Arrays.asList(home, about, productNav, caseNav, newsNav)) {
|
||||||
|
cmsNavigationService.save(nav);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 关于我们单页(path=about,status=1 已发布)
|
||||||
|
CmsPage aboutPage = new CmsPage();
|
||||||
|
aboutPage.setTitle("关于我们");
|
||||||
|
aboutPage.setPath("about");
|
||||||
|
aboutPage.setContent(buildAboutContent(companyName));
|
||||||
|
aboutPage.setDescription("了解" + companyName + "的发展历程、业务能力与核心价值观。");
|
||||||
|
aboutPage.setKeywords(companyName + ",关于我们,企业简介");
|
||||||
|
aboutPage.setStatus(1);
|
||||||
|
aboutPage.setTenantId(tenantId);
|
||||||
|
cmsPageService.save(aboutPage);
|
||||||
|
// 关联导航栏目(便于后台「单页管理」定位)
|
||||||
|
about.setPageId(aboutPage.getPageId());
|
||||||
|
cmsNavigationService.updateById(about);
|
||||||
|
|
||||||
|
// 3. 产品展示(6 条,categoryId = 产品栏目 navigationId,status=0 可见)
|
||||||
|
int pSort = 1;
|
||||||
|
for (String[] p : PRODUCT_DEMO) {
|
||||||
|
CmsProduct product = new CmsProduct();
|
||||||
|
product.setProductName(p[0]);
|
||||||
|
product.setSummary(p[1]);
|
||||||
|
product.setDescription(p[1]);
|
||||||
|
product.setCategoryId(productNav.getNavigationId());
|
||||||
|
product.setCategoryName(productNav.getTitle());
|
||||||
|
product.setPrice(BigDecimal.ZERO);
|
||||||
|
product.setOriginalPrice(BigDecimal.ZERO);
|
||||||
|
product.setUnit("套");
|
||||||
|
product.setStatus(0);
|
||||||
|
product.setSortNumber(pSort);
|
||||||
|
product.setTop(pSort <= 3 ? 1 : 0);
|
||||||
|
product.setTenantId(tenantId);
|
||||||
|
product.setUserId(userId);
|
||||||
|
cmsProductService.save(product);
|
||||||
|
pSort++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 客户案例(6 个,categoryId = 案例栏目 navigationId,status=0 可见)
|
||||||
|
int cSort = 1;
|
||||||
|
for (String[] c : CASE_DEMO) {
|
||||||
|
CmsCase cmsCase = new CmsCase();
|
||||||
|
cmsCase.setCaseName(c[0]);
|
||||||
|
cmsCase.setSummary(c[1]);
|
||||||
|
cmsCase.setContent(c[1]);
|
||||||
|
cmsCase.setCustomer(c[2]);
|
||||||
|
cmsCase.setIndustry(c[3]);
|
||||||
|
cmsCase.setCategoryId(caseNav.getNavigationId());
|
||||||
|
cmsCase.setCategoryName(caseNav.getTitle());
|
||||||
|
cmsCase.setStatus(0);
|
||||||
|
cmsCase.setSortNumber(cSort);
|
||||||
|
cmsCase.setTenantId(tenantId);
|
||||||
|
cmsCaseService.save(cmsCase);
|
||||||
|
cSort++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 新闻资讯(6 篇,categoryId = 资讯栏目 navigationId,status=0 已发布)
|
||||||
|
int aSort = 1;
|
||||||
|
for (String[] a : NEWS_DEMO) {
|
||||||
|
CmsArticle article = new CmsArticle();
|
||||||
|
article.setTitle(a[0].replace("{company}", companyName));
|
||||||
|
article.setModel("article");
|
||||||
|
article.setCategoryId(newsNav.getNavigationId());
|
||||||
|
article.setOverview(a[1].replace("{company}", companyName));
|
||||||
|
article.setStatus(0);
|
||||||
|
article.setSortNumber(aSort);
|
||||||
|
article.setUserId(userId);
|
||||||
|
article.setTenantId(tenantId);
|
||||||
|
article.setAuthor(companyName);
|
||||||
|
if (cmsArticleService.save(article)) {
|
||||||
|
CmsArticleContent content = new CmsArticleContent();
|
||||||
|
content.setArticleId(article.getArticleId());
|
||||||
|
content.setContent(a[1].replace("{company}", companyName));
|
||||||
|
content.setTenantId(tenantId);
|
||||||
|
cmsArticleContentService.save(content);
|
||||||
|
}
|
||||||
|
aSort++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 克隆模板站点的栏目与文章(历史默认行为,未传 initDemoData 时走此分支,兼容后台手动建站等场景)。
|
||||||
|
*/
|
||||||
|
private void cloneTemplateContent(CmsWebsite website, User loginUser, Integer websiteUserId) {
|
||||||
|
CmsNavigationParam cmsNavigationParam = new CmsNavigationParam();
|
||||||
|
cmsNavigationParam.setWebsiteUserId(websiteUserId);
|
||||||
|
cmsNavigationParam.setParentId(0);
|
||||||
|
final List<CmsNavigation> parents = cmsNavigationMapper.selectListAllRel(cmsNavigationParam);
|
||||||
|
parents.forEach(d -> {
|
||||||
|
Integer navigationId = d.getNavigationId();
|
||||||
|
d.setTenantId(loginUser.getTenantId());
|
||||||
|
d.setUserId(loginUser.getUserId());
|
||||||
|
if (cmsNavigationService.save(d)) {
|
||||||
|
cmsNavigationService.saveAsync(d);
|
||||||
|
CmsArticleParam cmsArticleParam = new CmsArticleParam();
|
||||||
|
cmsArticleParam.setWebsiteUserId(websiteUserId);
|
||||||
|
cmsArticleParam.setCategoryId(navigationId);
|
||||||
|
final List<CmsArticle> articles = cmsArticleMapper.selectListAllRel(cmsArticleParam);
|
||||||
|
articles.forEach(a -> {
|
||||||
|
a.setCategoryId(d.getNavigationId());
|
||||||
|
a.setUserId(loginUser.getUserId());
|
||||||
|
a.setTenantId(loginUser.getTenantId());
|
||||||
|
if (cmsArticleService.save(a)) {
|
||||||
|
CmsArticleContent content = new CmsArticleContent();
|
||||||
|
content.setArticleId(a.getArticleId());
|
||||||
|
content.setContent(a.getContent());
|
||||||
|
cmsArticleContentService.save(content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cmsNavigationParam.setParentId(navigationId);
|
||||||
|
final List<CmsNavigation> navigations = cmsNavigationMapper.selectListAllRel(cmsNavigationParam);
|
||||||
|
navigations.forEach(c -> {
|
||||||
|
cmsArticleParam.setCategoryId(c.getNavigationId());
|
||||||
|
c.setParentId(d.getNavigationId());
|
||||||
|
c.setTenantId(loginUser.getTenantId());
|
||||||
|
c.setUserId(loginUser.getUserId());
|
||||||
|
cmsNavigationService.save(c);
|
||||||
|
cmsNavigationService.saveAsync(c);
|
||||||
|
final List<CmsArticle> articles2 = cmsArticleMapper.selectListAllRel(cmsArticleParam);
|
||||||
|
articles2.forEach(a2 -> {
|
||||||
|
a2.setCategoryId(c.getNavigationId());
|
||||||
|
a2.setParentId(c.getParentId());
|
||||||
|
a2.setUserId(loginUser.getUserId());
|
||||||
|
a2.setTenantId(loginUser.getTenantId());
|
||||||
|
if (cmsArticleService.save(a2)) {
|
||||||
|
CmsArticleContent content = new CmsArticleContent();
|
||||||
|
content.setArticleId(a2.getArticleId());
|
||||||
|
content.setContent(a2.getContent());
|
||||||
|
cmsArticleContentService.save(content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 构造一个顶部导航栏目 */
|
||||||
|
private CmsNavigation buildNav(String title, String model, String path, int sort, int type,
|
||||||
|
Integer tenantId, Integer userId) {
|
||||||
|
CmsNavigation nav = new CmsNavigation();
|
||||||
|
nav.setTitle(title);
|
||||||
|
nav.setModel(model);
|
||||||
|
nav.setPath(path);
|
||||||
|
nav.setType(type);
|
||||||
|
nav.setParentId(0);
|
||||||
|
nav.setTop(1);
|
||||||
|
nav.setBottom(0);
|
||||||
|
nav.setPosition(1);
|
||||||
|
nav.setHide(0);
|
||||||
|
nav.setStatus(0);
|
||||||
|
nav.setSortNumber(sort);
|
||||||
|
nav.setTenantId(tenantId);
|
||||||
|
nav.setUserId(userId);
|
||||||
|
return nav;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 生成关于我们单页正文(富文本 HTML) */
|
||||||
|
private String buildAboutContent(String companyName) {
|
||||||
|
return "<p>欢迎来到 <strong>" + companyName + "</strong>!我们是一家专注于为企业提供数字化解决方案的服务商。</p>"
|
||||||
|
+ "<p>自成立以来," + companyName + "始终以客户成功为中心,围绕官网建设、内容管理与营销增长等场景,"
|
||||||
|
+ "提供从咨询、设计到落地运营的一站式服务。</p>"
|
||||||
|
+ "<p>我们的团队由资深产品、设计与技术专家组成,已为众多行业客户交付稳定可靠的数字平台。"
|
||||||
|
+ "期待与您携手,共创更大价值。</p>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- cms_website 补 clone_tenant_id 列(幂等版,兼容 MySQL 5.7 / 8.0)
|
||||||
|
-- 该列用于记录"克隆来源租户UID(=sys_user.template_id)",建站时写入,
|
||||||
|
-- 供后端按模板分类站点,并作为内容克隆来源。
|
||||||
|
-- 注:生产 modules.cms_website 通常已存在该列(websopy 建站写入),
|
||||||
|
-- 本脚本仅作为安全网,缺列才补,可重复执行。
|
||||||
|
-- 通过存储过程判断存在性,已存在则跳过;
|
||||||
|
-- 需在支持 DELIMITER 的客户端执行(mysql CLI、Navicat、MySQL Workbench 等)。
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS `add_cms_website_clone_tenant_id`$$
|
||||||
|
CREATE PROCEDURE `add_cms_website_clone_tenant_id`()
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='modules' AND TABLE_NAME='cms_website' AND COLUMN_NAME='clone_tenant_id') THEN
|
||||||
|
ALTER TABLE `cms_website` ADD COLUMN `clone_tenant_id` INT NULL COMMENT '克隆来源租户UID(=sys_user.template_id,建站时写入,供后端按模板分类)' AFTER `template_id`;
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
CALL `add_cms_website_clone_tenant_id`();
|
||||||
|
DROP PROCEDURE IF EXISTS `add_cms_website_clone_tenant_id`;
|
||||||
@@ -45,6 +45,9 @@ public class ShopGoodsSku implements Serializable {
|
|||||||
@Schema(description = "成本价")
|
@Schema(description = "成本价")
|
||||||
private BigDecimal cost;
|
private BigDecimal cost;
|
||||||
|
|
||||||
|
@Schema(description = "会员价")
|
||||||
|
private BigDecimal buyingPrice;
|
||||||
|
|
||||||
@Schema(description = "库存")
|
@Schema(description = "库存")
|
||||||
private Integer stock;
|
private Integer stock;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user