目标
利用redis实现类似百度首页新闻热榜功能。
功能
新闻排行榜以热度为指标降序排序,这里假设热度就是评论数量且统计的热度时间范围以当天为准;根据新闻的时效性,这里假设每15分钟刷新一次新闻榜单。
分析 zset数据类型:一个有序集合最多 个元素,集合元素有序不可重复,每个元素都会关联一个double类型的分数。元素根据分数从小到大的排序,分数可以重复。zscore命令可以对分数实现增量,且如果该zset中没有该元素,则会创建该条数据。可以将模块名+当天的时间作为zset的键,用户评论量作为分数,新闻标题作为值,每当用户评论一次新闻,分数则相应地加1。每隔15分钟提取新闻统计中的前30名(包含第30名)榜单,放入到新闻热榜的zset中。
代码实现
控制层
package com.shoppingcart.controller;
import com.shoppingcart.service.newstopserver;
import org.springframework.web.bind.annotation.*;
import javax.annotation.resource;
import java.util.list;
import java.util.map;
/**
* 新闻排行榜
*/
@restcontroller
@requestmapping("/newstop")
public class newstopcontroller {
@resource
public newstopserver newstopserver;
/**
* http://localhost:8099/newstop/zscorenews?newtitle=《欢乐喜剧人7》全新赛制养成新人&score=434000
* 创建新闻统计&实时统计新闻热度
* @param newtitle 新闻标题 (根据业务也可以写成新闻id)
* @param score 热度增量
* @return 给新闻一个增量以后,返回新闻的当前分数。
*/
@getmapping("/zscorenews")
public map<string, object> zscorenews(
@requestparam(value = "newtitle", required = true) string newtitle,
@requestparam(value = "score", defaultvalue = "1") double score
) {
map<string, object> map = newstopserver.incrementscore(newtitle, score);
return map;
}
/**
* http://localhost:8099/newstop/findnewbynewtitle?newtitle=《欢乐喜剧人7》全新赛制养成新人
* 查询某条新闻的热度
* @param newtitle
* @return
*/
@getmapping("/findnewbynewtitle")
public map<string, object> findnewbynewtitle(
@requestparam(value = "newtitle", required = true) string newtitle
) {
map<string, object> map = newstopserver.findnewbynewtitle(newtitle);
return map;
}
/**
* http://localhost:8099/newstop/createnewstop?startpage=0&endpage=29
* 对统计的新闻数据降序排序,并将[29,0]之间的数据放入新闻排行榜。(这个方法可以设置成定时任务。)
* @param startpage 开始下标
* @param endpage 结束下标
* @return
*/
@getmapping("/createnewstop")
public map<string, object> createnewstop(
@requestparam(value = "startpage", defaultvalue = "0") int startpage,
@requestparam(value = "endpage", defaultvalue = "29") int endpage
) {
map<string, object> map = newstopserver.createnewstop(startpage, endpage);
return map;
}
/**
* http://localhost:8099/newstop/newstop?startpage=20&endpage=29
* 对统计的新闻数据降序排序,并将[29,0]之间的数据放入新闻排行榜。(这个方法可以设置成定时任务。)
*
* @param startpage 开始下标
* @param endpage 结束下标
* @return
*/
@getmapping("/newstop")
public map<string, object> newstop(
@requestparam(value = "startpage", defaultvalue = "0") int startpage,
@requestparam(value = "endpage", defaultvalue = "9") int endpage
) {
map<string, object> map = newstopserver.newstop(startpage, endpage);
return map;
}
/**
* http://localhost:8099/newstop/addtestdata
* 批量增加测试数据(新闻统计)
*/
@postmapping("/addtestdata")
public void addtestdata(@requestbody list<map<string, object>> list) {
for (int i = 0; i < list.size(); i++) {
system.out.println(list.get(i).get("value").tostring());
system.out.println(double.parsedouble(list.get(i).get("score").tostring()));
zscorenews(list.get(i).get("value").tostring(), double.parsedouble(list.get(i).get("score").tostring()));
}
}
/**新增测试数据:
[
{
"score": 2356428.0,
"value": "《蒙面唱将猜猜猜》第五季收官"
},
{
"score": 2335456.0,
"value": "《欢乐喜剧人7》全新赛制养成新人"
},
{
"score": 987655.0,
"value": "《星光大道》2020年度总决赛"
},
{
"score": 954566.0,
"value": "网易北京:重构梦幻西游项目"
},
{
"score": 943665.0,
"value": "神武惊现靓号:44488888"
},
{
"score": 876653.0,
"value": "小米手机:红米"
},
{
"score": 875444.0,
"value": "英特尔扩大外包"
},
{
"score": 755656.0,
"value": "多益广州举办神武4手游比赛"
},
{
"score": 687466.0,
"value": "亮剑重播超记录"
},
{
"score": 567645.0,
"value": "春节快到了"
},
{
"score": 554342.0,
"value": "购票狂潮"
},
{
"score": 466654.0,
"value": "达摩院旗下拥有20多位世界级的科学家"
},
{
"score": 456666.0,
"value": "nba mvp候选人"
},
{
"score": 435654.0,
"value": "cba最佳新秀"
},
{
"score": 392875.0,
"value": "数字货币新时代"
},
{
"score": 300454.0,
"value": "网易新手游即将发布"
},
{
"score": 277654.0,
"value": "cba12强排名:四强格局已定"
},
{
"score": 265656.0,
"value": "用黑科技悄悄改变大众生活"
},
{
"score": 234665.0,
"value": "玉溪:致力打造全省数字经济第一城"
},
{
"score": 234665.0,
"value": "广西培育消费新业态新模式"
},
{
"score": 234656.0,
"value": "互联网产品是顺从用户?还是教育用户?"
},
{
"score": 234564.0,
"value": "蒋军:企业做强,做大跟产品的关系是什么?"
},
{
"score": 234564.0,
"value": "热搜第一!微信又有重大更新,这次有点炸"
},
{
"score": 234555.0,
"value": "成功的人,往往都读这“6”种书"
},
{
"score": 134566.0,
"value": "外地职工留苏州过年 落户加15分"
},
{
"score": 133455.0,
"value": "蒋军:成功创业的7种思维!创业者必读!"
},
{
"score": 98554.0,
"value": "阿里平头哥:首个risc - v版安卓10系统顺畅运行"
},
{
"score": 87654.0,
"value": "不断增强人民群众就医获得感"
},
{
"score": 54347.0,
"value": "《星光大道》年度总冠军出炉"
},
{
"score": 43335.0,
"value": "流量应是榜样,榜样应成力量"
},
{
"score": 23555.0,
"value": "《山海情》:主旋律可以这样好看"
},
{
"score": 23456.0,
"value": "2021艺考新动向"
}
]
*/
}
业务层
package com.shoppingcart.service;
import java.util.map;
public interface newstopserver {
map<string, object> incrementscore(string newtitle,double zscore);
map<string, object> findnewbynewtitle(string newtitle);
map<string, object> createnewstop(int startpage, int endpage);
map<string, object> newstop(int startpage, int endpage);
}
package com.shoppingcart.service.impl;
import com.shoppingcart.service.newstopserver;
import com.shoppingcart.utils.redisservice;
import org.springframework.data.redis.core.zsetoperations;
import org.springframework.stereotype.service;
import javax.annotation.resource;
import java.util.*;
@service
public class newstopserverimpl implements newstopserver {
@resource
private redisservice redisservice;
@override
public map<string, object> incrementscore(string newtitle, double score) {
map<string, object> map = new hashmap<>();
//string key= "newssta:"+dateutils.datetostring(new date(),"yyyymmdd");
string key = "newssta:" + "20210123";
double d = redisservice.incrementscore(key, newtitle, score);
map<string, object> m = new hashmap<string, object>() {
{
put("key", key);
put("newtitle", newtitle);
put("score", d);
}
};
map.put("data", m);
map.put("code", 0);
return map;
}
@override
public map<string, object> findnewbynewtitle(string newtitle) {
//string key= "newssta:"+dateutils.datetostring(new date(),"yyyymmdd");
string key = "newssta:" + "20210123";
double d = redisservice.score(key, newtitle);
map<string, object> map = new hashmap<>();
map<string, object> m = new hashmap<string, object>() {
{
put("key", key);
put("newtitle", newtitle);
put("score", d);
}
};
map.put("data", m);
map.put("code", 0);
return map;
}
/**
* @param startpage
* @param endpage
* @return
*/
@override
public map<string, object> createnewstop(int startpage, int endpage) {
map<string, object> map = new hashmap<>();
//新闻统计键
//string newsstakey= "newssta:"+dateutils.datetostring(new date(),"yyyymmdd");
string newsstakey = "newssta:" + "20210123";
//新闻前30排名键
//string newstopkey= "newssta:"+dateutils.datetostring(new date(),"yyyymmdd");
string newstopkey = "newstop:" + "20210123";
//查询前30的信息(interface comparable<t> :该接口对实现它的每个类的对象强加一个整体排序。)
set<zsetoperations.typedtuple<object>> set = redisservice.reverserangewithscores(newsstakey, startpage, endpage);
if (set == null || set.size() == 0) {
map.put("data", null);
map.put("code", 1);
return map;
}
//删除旧的新闻排行榜
redisservice.del(newstopkey);
//添加新闻排行榜数据
long zsetsize = redisservice.zsetadd(newstopkey, set);
map<string, object> m = new hashmap<string, object>() {
{
put("data", set);
put("size", zsetsize);
}
};
map.put("data", m);
map.put("code", 0);
return map;
}
/**
* 查看新闻热榜(top30)
*
* @param startpage
* @param endpage
* @return
*/
@override
public map<string, object> newstop(int startpage, int endpage) {
//新闻统计键
//string newsstakey= "newssta:"+dateutils.datetostring(new date(),"yyyymmdd");
string newsstakey = "newssta:" + "20210123";
//新闻前30排名键
//string newstopkey= "newssta:"+dateutils.datetostring(new date(),"yyyymmdd");
string newstopkey = "newstop:" + "20210123";
set<zsetoperations.typedtuple<object>> set = redisservice.reverserangewithscores(newstopkey, startpage, endpage);
map<string, object> m = new hashmap<string, object>();
m.put("data", set);
m.put("size", set.size());
//新闻排行榜为空,也许现在正在添加数据,先查询新闻统计键。
if (set == null || set.size() == 0) {
//查询前30的信息(interface comparable<t> :该接口对实现它的每个类的对象强加一个整体排序。)
set<zsetoperations.typedtuple<object>> set2 = redisservice.reverserangewithscores(newsstakey, startpage, endpage);
m.put("data", set);
m.put("size", set.size());
}
map<string, object> map = new hashmap<>();
map.put("data", m);
map.put("code", 0);
return map;
}
}
工具类
package com.shoppingcart.utils;
import java.text.simpledateformat;
import java.util.date;
public class dateutils {
// 日期转字符串,返回指定的格式
public static string datetostring(date date, string dateformat) {
simpledateformat sdf = new simpledateformat(dateformat);
return sdf.format(date);
}
}
package com.shoppingcart.utils;
import com.alibaba.fastjson.jsonobject;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.data.redis.connection.rediszsetcommands;
import org.springframework.data.redis.connection.sortparameters;
import org.springframework.data.redis.core.defaulttypedtuple;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.core.zsetoperations;
import org.springframework.stereotype.service;
import org.springframework.util.collectionutils;
import org.w3c.dom.ranges.range;
import java.util.*;
import java.util.concurrent.timeunit;
import java.util.stream.collectors;
@service
public class redisservice {
@autowired
private redistemplate<string, object> redistemplate;
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(string key, long time) {
try {
if (time > 0) {
redistemplate.expire(key, time, timeunit.seconds);
}
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getexpire(string key) {
return redistemplate.getexpire(key, timeunit.seconds);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean haskey(string key) {
try {
return redistemplate.haskey(key);
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@suppresswarnings("unchecked")
public void del(string... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redistemplate.delete(key[0]);
} else {
list<string> list = new arraylist<>(arrays.aslist(key));
redistemplate.delete(list);
}
}
}
/**
* 删除缓存
*
* @param keys 可以传一个值 或多个
*/
@suppresswarnings("unchecked")
public void del(collection keys) {
if (org.apache.commons.collections4.collectionutils.isnotempty(keys)) {
redistemplate.delete(keys);
}
}
// ============================string=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public object get(string key) {
return key == null ? null : redistemplate.opsforvalue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(string key, object value) {
try {
redistemplate.opsforvalue().set(key, value);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(string key, object value, long time) {
try {
if (time > 0) {
redistemplate.opsforvalue().set(key, value, time, timeunit.seconds);
} else {
set(key, value);
}
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(string key, long delta) {
if (delta < 0) {
throw new runtimeexception("递增因子必须大于0");
}
return redistemplate.opsforvalue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(string key, long delta) {
if (delta < 0) {
throw new runtimeexception("递减因子必须大于0");
}
return redistemplate.opsforvalue().increment(key, -delta);
}
// ================================hash=================================
/**
* hashget
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public object hget(string key, string item) {
return redistemplate.opsforhash().get(key, item);
}
/**
* 获取hashkey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public map<object, object> hmget(string key) {
map<object, object> entries = redistemplate.opsforhash().entries(key);
return entries;
}
/**
* hashset
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(string key, map<string, object> map) {
try {
redistemplate.opsforhash().putall(key, map);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* hashset 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(string key, map<string, object> map, long time) {
try {
redistemplate.opsforhash().putall(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在则不添加。
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hsetnx(string key, string item, object value) {
try {
boolean success = redistemplate.opsforhash().putifabsent(key, item, value);
return success;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果存在就覆盖原来的值。
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(string key, string item, object value) {
try {
redistemplate.opsforhash().put(key, item, value);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果存在就覆盖原来的值。
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(string key, string item, object value, long time) {
try {
redistemplate.opsforhash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
* 返回被删除的数量
*/
public long hdel(string key, object... item) {
return redistemplate.opsforhash().delete(key, item);
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param items 项 可以使多个 不能为null
*/
public void hdel(string key, collection items) {
redistemplate.opsforhash().delete(key, items.toarray());
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hhaskey(string key, string item) {
return redistemplate.opsforhash().haskey(key, item);
}
/**
* hash数据类型:给元素一个增量 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param delta 要增加几(大于0)
* @return
*/
public double hincr(string key, string item, double delta) {
return redistemplate.opsforhash().increment(key, item, delta);
}
// ============================set=============================
/**
* 根据key获取set中的所有值
*
* @param key 键
* @return
*/
public set<object> sget(string key) {
try {
return redistemplate.opsforset().members(key);
} catch (exception e) {
e.printstacktrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean shaskey(string key, object value) {
try {
return redistemplate.opsforset().ismember(key, value);
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sset(string key, object... values) {
try {
return redistemplate.opsforset().add(key, values);
} catch (exception e) {
e.printstacktrace();
return 0;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sset(string key, collection values) {
try {
return redistemplate.opsforset().add(key, values.toarray());
} catch (exception e) {
e.printstacktrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long ssetandtime(string key, long time, object... values) {
try {
long count = redistemplate.opsforset().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (exception e) {
e.printstacktrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sgetsetsize(string key) {
try {
return redistemplate.opsforset().size(key);
} catch (exception e) {
e.printstacktrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setremove(string key, object... values) {
try {
long count = redistemplate.opsforset().remove(key, values);
return count;
} catch (exception e) {
e.printstacktrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public list<object> lget(string key, long start, long end) {
try {
return redistemplate.opsforlist().range(key, start, end);
} catch (exception e) {
e.printstacktrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lgetlistsize(string key) {
try {
return redistemplate.opsforlist().size(key);
} catch (exception e) {
e.printstacktrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public object lgetindex(string key, long index) {
try {
return redistemplate.opsforlist().index(key, index);
} catch (exception e) {
e.printstacktrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lset(string key, object value) {
try {
redistemplate.opsforlist().rightpush(key, value);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lset(string key, object value, long time) {
try {
redistemplate.opsforlist().rightpush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lset(string key, list<object> value) {
try {
redistemplate.opsforlist().rightpushall(key, value);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lset(string key, list<object> value, long time) {
try {
redistemplate.opsforlist().rightpushall(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lupdateindex(string key, long index, object value) {
try {
redistemplate.opsforlist().set(key, index, value);
return true;
} catch (exception e) {
e.printstacktrace();
return false;
}
}
/**
* 移除n个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lremove(string key, long count, object value) {
try {
long remove = redistemplate.opsforlist().remove(key, count, value);
return remove;
} catch (exception e) {
e.printstacktrace();
return 0;
}
}
// ===============================zset=================================
/**
* 给key键的value增加value分数,没有则会创建。
*
* @param key 键
* @param value 值
* @param score 分数
*/
public double incrementscore(string key, string value, double score) {
//boolean add = redistemplate.boundzsetops(key).add(value, score);
double add = redistemplate.boundzsetops(key).incrementscore(value, score);
return add;
}
/**
* 获得指定zset元素的分数
*
* @param key
* @param value
* @return
*/
public double score(string key, string value) {
double score = redistemplate.boundzsetops(key).score(value);
return score;
}
/**
* 升序查询key集合内[endtop,starttop]如果是负数表示倒数
* endtop=-1,starttop=0表示获取所有数据。
*
* @param key
* @param startpage
* @param endpage
*/
public set<zsetoperations.typedtuple<object>> rangewithscores(string key, int startpage, int endpage) {
set<zsetoperations.typedtuple<object>> set = redistemplate.boundzsetops(key).rangewithscores(startpage, endpage);
return set;
}
/**
* 降序查询key集合内[endtop,starttop],如果是负数表示倒数
* endtop=-1,starttop=0表示获取所有数据。
*
* @param key
* @param startpage
* @param endpage
*/
public set<zsetoperations.typedtuple<object>> reverserangewithscores(string key, int startpage, int endpage) {
set<zsetoperations.typedtuple<object>> set = redistemplate.boundzsetops(key).reverserangewithscores(startpage, endpage);
return set;
}
/**
* 批量新增数据
*
* @param key
* @param set
* @return
*/
public long zsetadd(string key, set set) {
long add = redistemplate.boundzsetops(key).add(set);
return add;
}
/**
* 删除指定键的指定下标范围数据
*
* @param key
* @param startpage
* @param endpage
*/
public long zsetremoverange(string key, int startpage, int endpage) {
long l = redistemplate.boundzsetops(key).removerange(startpage, endpage);
return l;
}
/**
* 删除指定键的指定值
*
* @param key
* @param value
*/
public long zsetremove(string key, string value) {
long remove = redistemplate.boundzsetops(key).remove(value);
return remove;
}
}
到此这篇关于redis实战之百度首页新闻热榜的文章就介绍到这了,更多相关redis百度首页新闻热榜内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!