feat: improve nursing article module with rich content and view tracking

- Add content field (LONGTEXT) with Quill rich text editor in admin UI
- Add view_count field with auto-increment API endpoint
- Replace manual image URL input with image-upload component
- Add /system/nursing/published and /view/{id} public endpoints
- Improve Android error handling with toast and cache fallback
- Optimize ViewHolderFive adapter to reuse instead of recreating

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
renjianbo
2026-05-23 00:52:12 +08:00
parent a53ed96119
commit 12d67c00f4
10 changed files with 125 additions and 37 deletions

View File

@@ -145,7 +145,7 @@ public class HomeFragment extends BaseFragment {
private void initNursingInfo() {
OkGo.<String>get(HttpConstants.URi_system_getAppIndexInfo)
.converter(new StringConvert())
.cacheMode(CacheMode.NO_CACHE)
.cacheMode(CacheMode.REQUEST_FAILED_READ_CACHE)
.adapt(new ObservableResponse<String>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@@ -181,6 +181,7 @@ public class HomeFragment extends BaseFragment {
@Override
public void onError(@NonNull Throwable e) {
e.printStackTrace();
toast("加载护理资讯失败");
}
@Override

View File

@@ -32,10 +32,12 @@ import de.greenrobot.event.EventBus;
public class ViewHolderFive extends AbstractViewTypeHolder {
private final RecyclerView style_recyleview;
private BaseQuickAdapter<homeListBean, BaseViewHolder> mAdapter;
public ViewHolderFive(View itemView) {
super(itemView);
style_recyleview = (RecyclerView) itemView.findViewById(R.id.reply_rcey);
style_recyleview.setLayoutManager(new LinearLayoutManager(itemView.getContext()));
}
@Override
@@ -43,36 +45,40 @@ public class ViewHolderFive extends AbstractViewTypeHolder {
super.bindHolder(dataBean, position, mContext, mviewListener);
//护理资讯
LinearLayoutManager manager = new LinearLayoutManager(mContext);
style_recyleview.setLayoutManager(manager);
List<homeListBean> data = dataBean.getNursingInfoList();
if (data == null) {
data = new ArrayList<>();
}
final List<homeListBean> finalData = data;
style_recyleview.setAdapter(new BaseQuickAdapter<homeListBean, BaseViewHolder>(R.layout.home_list_item, data) {
@Override
protected void convert(final BaseViewHolder helper, final homeListBean item) {
WidgetTools.setTextfive((TextView) helper.getView(R.id.img_modify), "", helper.getAdapterPosition() + 1 + "." + item.getContent());
GlideTools.init(mContext).displaypic((ImageView) helper.getView(R.id.home_img_01), item.getPic(), R.mipmap.home_bj01);
RelativeLayout rl_style = (RelativeLayout)helper.getView(R.id.reply_dialog_rl);
rl_style.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!ProfileSpUtils.getInstance().isLogin()){
Intent intent =new Intent(mContext, NewCodeLoginActivity.class);
mContext.startActivity(intent);
return;
if (mAdapter == null) {
mAdapter = new BaseQuickAdapter<homeListBean, BaseViewHolder>(R.layout.home_list_item, data) {
@Override
protected void convert(final BaseViewHolder helper, final homeListBean item) {
WidgetTools.setTextfive((TextView) helper.getView(R.id.img_modify), "", helper.getAdapterPosition() + 1 + "." + item.getContent());
GlideTools.init(mContext).displaypic((ImageView) helper.getView(R.id.home_img_01), item.getPic(), R.mipmap.home_bj01);
RelativeLayout rl_style = (RelativeLayout) helper.getView(R.id.reply_dialog_rl);
rl_style.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!ProfileSpUtils.getInstance().isLogin()) {
Intent intent = new Intent(mContext, NewCodeLoginActivity.class);
mContext.startActivity(intent);
return;
}
String url = item.getUrl();
if (url != null && !url.isEmpty()) {
XfiveWebActivity.runActivity(mContext, "了解陪护", url);
} else {
XfiveWebActivity.runActivity(mContext, "了解陪护", "file:///android_asset/privacy.html");
}
}
String url = item.getUrl();
if (url != null && !url.isEmpty()) {
XfiveWebActivity.runActivity(mContext, "了解陪护", url);
} else {
XfiveWebActivity.runActivity(mContext, "了解陪护", "file:///android_asset/privacy.html");
}
}
});
}
});
});
}
};
style_recyleview.setAdapter(mAdapter);
} else {
mAdapter.setNewData(finalData);
}
}
}

View File

@@ -73,11 +73,12 @@
<el-table-column label="标题" align="center" prop="title" :show-overflow-tooltip="true" />
<el-table-column label="封面图片" align="center" prop="imageUrl" width="100">
<template slot-scope="scope">
<el-image v-if="scope.row.imageUrl" :src="scope.row.imageUrl" style="width:50px;height:50px;" fit="cover" />
<image-preview v-if="scope.row.imageUrl" :src="scope.row.imageUrl" :width="50" :height="50" />
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="文章链接" align="center" prop="articleUrl" :show-overflow-tooltip="true" />
<el-table-column label="阅读量" align="center" prop="viewCount" width="80" />
<el-table-column label="状态" align="center" prop="status" width="80">
<template slot-scope="scope">
<el-tag :type="scope.row.status == '0' ? 'success' : 'danger'" size="small">
@@ -120,10 +121,13 @@
<el-input v-model="form.title" placeholder="请输入标题" />
</el-form-item>
<el-form-item label="封面图片" prop="imageUrl">
<el-input v-model="form.imageUrl" placeholder="请输入图片URL地址" />
<image-upload v-model="form.imageUrl" :limit="1" />
</el-form-item>
<el-form-item label="文章链接" prop="articleUrl">
<el-input v-model="form.articleUrl" placeholder="请输入文章链接URL地址" />
<el-input v-model="form.articleUrl" placeholder="请输入文章链接URL地址(可选)" />
</el-form-item>
<el-form-item label="文章正文" prop="content">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
<el-form-item label="排序" prop="sortOrder">
<el-input-number v-model="form.sortOrder" :min="0" style="width: 100%" placeholder="请输入排序号" />
@@ -174,12 +178,6 @@ export default {
{ required: true, message: "标题不能为空", trigger: "blur" }
],
imageUrl: [
{ required: true, message: "封面图片不能为空", trigger: "blur" }
],
articleUrl: [
{ required: true, message: "文章链接不能为空", trigger: "blur" }
],
status: [
{ required: true, message: "请选择状态", trigger: "change" }
],
}
@@ -207,6 +205,7 @@ export default {
title: null,
imageUrl: null,
articleUrl: null,
content: null,
sortOrder: 0,
status: '0',
remark: null,

View File

@@ -109,7 +109,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login","/appLogin","/system/msm","/smsLogin", "/register", "/captchaImage","/weixinLogin","/getUserToken","/system/weixin/wxPayNotify","/system/weixin/wxRefundNotify","/system/user/getAppIndexInfo").anonymous()
.antMatchers("/login","/appLogin","/system/msm","/smsLogin", "/register", "/captchaImage","/weixinLogin","/getUserToken","/system/weixin/wxPayNotify","/system/weixin/wxRefundNotify","/system/user/getAppIndexInfo","/system/nursing/published","/system/nursing/view/**").anonymous()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()

View File

@@ -20,6 +20,8 @@ import com.ruoyi.system.domain.RlzNursingArticle;
import com.ruoyi.system.service.IRlzNursingArticleService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
/**
* 护理资讯Controller
@@ -91,6 +93,39 @@ public class RlzNursingArticleController extends BaseController
return toAjax(rlzNursingArticleService.updateRlzNursingArticle(rlzNursingArticle));
}
/**
* 获取已发布的护理资讯列表无需认证供App使用
*/
@GetMapping("/published")
public AjaxResult publishedList()
{
List<RlzNursingArticle> list = rlzNursingArticleService.selectPublishedList();
JSONArray arr = new JSONArray();
if (list != null) {
for (RlzNursingArticle a : list) {
JSONObject item = new JSONObject();
item.put("id", a.getId());
item.put("title", a.getTitle());
item.put("realTimeInfoImage", a.getImageUrl());
item.put("realTimeInfoUrl", a.getArticleUrl());
item.put("content", a.getContent());
item.put("viewCount", a.getViewCount());
arr.add(item);
}
}
return AjaxResult.success(arr);
}
/**
* 阅读量+1无需认证App点击文章时调用
*/
@PutMapping("/view/{id}")
public AjaxResult incrementView(@PathVariable("id") Long id)
{
rlzNursingArticleService.incrementViewCount(id);
return AjaxResult.success();
}
/**
* 删除护理资讯
*/

View File

@@ -30,6 +30,9 @@ public class RlzNursingArticle extends BaseEntity
@Excel(name = "文章链接")
private String articleUrl;
/** 文章正文(HTML) */
private String content;
/** 排序 */
private Integer sortOrder;
@@ -37,6 +40,9 @@ public class RlzNursingArticle extends BaseEntity
@Excel(name = "状态", readConverterExp = "0=发布,1=隐藏")
private String status;
/** 阅读次数 */
private Long viewCount;
public void setId(Long id)
{
this.id = id;
@@ -77,6 +83,16 @@ public class RlzNursingArticle extends BaseEntity
return articleUrl;
}
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return content;
}
public void setSortOrder(Integer sortOrder)
{
this.sortOrder = sortOrder;
@@ -97,6 +113,16 @@ public class RlzNursingArticle extends BaseEntity
return status;
}
public void setViewCount(Long viewCount)
{
this.viewCount = viewCount;
}
public Long getViewCount()
{
return viewCount;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -104,8 +130,10 @@ public class RlzNursingArticle extends BaseEntity
.append("title", getTitle())
.append("imageUrl", getImageUrl())
.append("articleUrl", getArticleUrl())
.append("content", getContent())
.append("sortOrder", getSortOrder())
.append("status", getStatus())
.append("viewCount", getViewCount())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())

View File

@@ -24,4 +24,6 @@ public interface RlzNursingArticleMapper
public int deleteRlzNursingArticleById(Long id);
public int deleteRlzNursingArticleByIds(Long[] ids);
public int incrementViewCount(Long id);
}

View File

@@ -24,4 +24,6 @@ public interface IRlzNursingArticleService
public int deleteRlzNursingArticleByIds(Long[] ids);
public int deleteRlzNursingArticleById(Long id);
public int incrementViewCount(Long id);
}

View File

@@ -63,4 +63,10 @@ public class RlzNursingArticleServiceImpl implements IRlzNursingArticleService
{
return rlzNursingArticleMapper.deleteRlzNursingArticleById(id);
}
@Override
public int incrementViewCount(Long id)
{
return rlzNursingArticleMapper.incrementViewCount(id);
}
}

View File

@@ -9,8 +9,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="title" column="title" />
<result property="imageUrl" column="image_url" />
<result property="articleUrl" column="article_url" />
<result property="content" column="content" />
<result property="sortOrder" column="sort_order" />
<result property="status" column="status" />
<result property="viewCount" column="view_count" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
@@ -19,7 +21,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectRlzNursingArticleVo">
select id, title, image_url, article_url, sort_order, status,
select id, title, image_url, article_url, cast(content as char) as content, sort_order, status, view_count,
create_by, create_time, update_by, update_time, remark
from rlz_nursing_article
</sql>
@@ -50,6 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="title != null">title,</if>
<if test="imageUrl != null">image_url,</if>
<if test="articleUrl != null">article_url,</if>
<if test="content != null">content,</if>
<if test="sortOrder != null">sort_order,</if>
<if test="status != null">status,</if>
<if test="createBy != null">create_by,</if>
@@ -60,6 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="title != null">#{title},</if>
<if test="imageUrl != null">#{imageUrl},</if>
<if test="articleUrl != null">#{articleUrl},</if>
<if test="content != null">#{content},</if>
<if test="sortOrder != null">#{sortOrder},</if>
<if test="status != null">#{status},</if>
<if test="createBy != null">#{createBy},</if>
@@ -74,6 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="title != null">title = #{title},</if>
<if test="imageUrl != null">image_url = #{imageUrl},</if>
<if test="articleUrl != null">article_url = #{articleUrl},</if>
<if test="content != null">content = #{content},</if>
<if test="sortOrder != null">sort_order = #{sortOrder},</if>
<if test="status != null">status = #{status},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
@@ -87,6 +92,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
delete from rlz_nursing_article where id = #{id}
</delete>
<update id="incrementViewCount" parameterType="Long">
update rlz_nursing_article set view_count = view_count + 1 where id = #{id}
</update>
<delete id="deleteRlzNursingArticleByIds" parameterType="String">
delete from rlz_nursing_article where id in
<foreach item="id" collection="array" open="(" separator="," close=")">