|
package com.ekexiu.portal.question;
import com.ekexiu.portal.notify.NotifyService;
import com.ekexiu.portal.notify.NotifyType;
import com.ekexiu.portal.util.SqlUtil;
import org.jfw.apt.annotation.Autowrie;
import org.jfw.apt.annotation.DefaultValue;
import org.jfw.apt.web.annotation.Path;
import org.jfw.apt.web.annotation.operate.Get;
import org.jfw.apt.web.annotation.operate.Post;
import org.jfw.apt.web.annotation.param.*;
import org.jfw.util.ListUtil;
import org.jfw.util.StringUtil;
import org.jfw.util.exception.JfwBaseException;
import org.jfw.util.jdbc.JdbcUtil;
import org.jfw.util.jdbc.PreparedStatementConfig;
import org.jfw.util.web.fileupload.Item;
import org.jfw.util.web.fileupload.UploadItemIterator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@Path("/question")
public class Service {
private static final AtomicInteger FN_IDX = new AtomicInteger(1);
@Autowrie
private QuestionDao questionDao;
@Autowrie
private NotifyService notifyService;
private File imgPath;
public NotifyService getNotifyService() {
return notifyService;
}
public void setNotifyService(NotifyService notifyService) {
this.notifyService = notifyService;
}
public File getImgPath() {
return imgPath;
}
public void setImgPath(File imgPath) {
this.imgPath = imgPath;
}
public QuestionDao getQuestionDao() {
return questionDao;
}
public void setQuestionDao(QuestionDao questionDao) {
this.questionDao = questionDao;
}
/**
* 上传文件
*
* @param it
* @return
* @throws Exception
*/
@Path("/upload")
@Post
public List<UploadFile> upload(@Upload UploadItemIterator it) throws Exception {
List<UploadFile> ret = new LinkedList<UploadFile>();
try {
while (it.hasNext()) {
Item item = it.next();
if (!item.isFormField()) {
String name = normalizeFileName(item.getName());
int index = name.lastIndexOf('.');
String ext = index >= 0 ? name.substring(index + 1) : "";
ext = ext.trim();
long fileLen = 0;
int len = 0;
UploadFile uf = this.buildTargetFile(ext);
uf.setName(name);
File file = uf.getFn();
ret.add(uf);
byte[] buf = new byte[8092];
OutputStream of = new FileOutputStream(file);
try {
InputStream in = item.getInputStream();
try {
while ((len = in.read(buf)) >= 0) {
if (len > 0) {
of.write(buf, 0, len);
fileLen += len;
}
}
uf.setSize(fileLen);
} finally {
in.close();
}
} finally {
of.close();
}
}
}
return ret;
} catch (Exception e) {
this.remove(ret);
throw e;
} finally {
if (it != null)
it.clean();
}
}
/**
* 新增一提问
*
* @param con
* @param q
* title 标题 ,cnt 描述 img 图片 以英文逗号分隔, keys 关键字 以英文逗号分隔 ,uid 用户ID
* @return
* @throws SQLException
*/
@Path()
@Post
public String save(@JdbcConn(true) Connection con,
@RequestParam(fields = { @FieldParam(value = "title", valueClass = String.class),
@FieldParam(value = "cnt", valueClass = String.class, required = false),
@FieldParam(value = "img", valueClass = String.class, required = false), @FieldParam(value = "keys", valueClass = String.class),
@FieldParam(value = "uid", valueClass = String.class) }) Question q)
throws SQLException {
String id = StringUtil.buildUUID();
q.setId(id);
q.setReplyCount(0);
q.setState("1");
List<String> kws = ListUtil.splitTrimExcludeEmpty(q.getKeys(), ',');
if (kws.isEmpty())
throw new IllegalArgumentException("param keys invalid");
questionDao.insert(con, q);
questionDao.insert(con, build(id, kws));
return id;
}
/**
* 默认的邀请专家
*
* @param con
* @param id
* 提问ID
* @param uid
* 本人ID
* @param count
* limit 查询 返回最后一条的 kws ,首次不传
* @param pid
* limit 查询 返回最后一条的 id 首次可不传
* @param rows
* limit查询返回的最大数据条数
* @return
* @throws SQLException
*/
@Get
@Path("/commendatoryPro")
public List<Map<String, Object>> professor(@JdbcConn Connection con, final String id,final String uid, final @DefaultValue("Integer.MAX_VALUE") int count,
final @DefaultValue("\"0\"") String pid, final @DefaultValue("Integer.MAX_VALUE") int rows) throws SQLException {
return JdbcUtil.queryMaps(con,
"select id, kws from(select id,count(1) kws from pro_key_word where id <> ? and kw in (select kw from qet_key_word where id =?) group by id ) t where kws<= ? and id >? order by kws desc,id asc limit ?",
new PreparedStatementConfig() {
@Override
public void config(PreparedStatement ps) throws SQLException {
ps.setString(1,uid);
ps.setString(2, id);
ps.setInt(3, count);
ps.setString(4, pid);
ps.setInt(5, rows);
}
});
}
/**
* 邀请专家回答提问
*
* @param con
* @param qid
* 提问id
* @param pid
* 专家id(被邀请人)
* @param uid
* 操作人(邀请人
* @param uname
* 操作人(邀请人)姓名
* @return
* @throws SQLException
* @throws JfwBaseException
*/
@Post
@Path("/invite")
public boolean invite(@JdbcConn(true) Connection con, String qid, String pid, String uid, String uname,@AfterCommit List<Runnable> runs) throws SQLException, JfwBaseException {
Question q = questionDao.query(con, qid);
if (q == null) {
throw new JfwBaseException(50000, "question[" +qid + "] not found");
}
QuestionInviteRec rec = new QuestionInviteRec();
rec.setUid(uid);
rec.setPid(pid);
rec.setQid(qid);
try{
questionDao.insert(con,rec);
}catch(SQLException e){
if(SqlUtil.unique(e)){
throw new JfwBaseException(50001, "duplicate invite");
}
throw e;
}
this.notifyService.notify(con, pid, uid, uname, qid, q.getTitle(), NotifyType.INVITE_ANSWER, runs);
return true;
}
@Get
@Path("/invite")
public List<QuestionInviteRec> queryInviteRec(@JdbcConn Connection con,String uid,String qid,String[] pid)throws SQLException{
return questionDao.queryInviteRec(con, qid, uid, pid);
}
/**
* 回答一个提问
*
* @param con
* @param qid
* 提问ID
* @param cnt
* 回答内容
* @param uid
* 回答 人ID
* @param uname
* 回答人姓名
* @return
* @throws SQLException
* @throws JfwBaseException
*/
@Post
@Path("/answer")
public String answer(@JdbcConn(true) Connection con, String qid, String cnt, String uid, String uname, @AfterCommit List<Runnable> runs)
throws SQLException, JfwBaseException {
Question q = questionDao.query(con, qid);
if (q == null) {
throw new JfwBaseException(50000, "question[" + qid + "] not found");
}
String id = StringUtil.buildUUID();
Answer a = new Answer();
a.setId(id);
a.setAgree(0);
a.setBallot(0);
a.setCnt(cnt);
a.setQid(qid);
a.setState("1");
a.setUid(uid);
try {
questionDao.insert(con, a);
} catch (SQLException e) {
if (SqlUtil.unique(e)) {
throw new JfwBaseException(50001, "duplicate answer");
}
throw e;
}
questionDao.incQuestionReply(con, qid);
notifyService.notify(con, q.getUid(), uid, uname, qid, q.getTitle(), NotifyType.ANSWER_QUESTION, runs);
return id;
}
@Get
@Path("/answer")
public Answer answer(@JdbcConn Connection con,String id)throws SQLException{
return questionDao.queryAnswer(con, id);
}
@Get
@Path("/answer/exists")
public boolean existsAnswer(@JdbcConn Connection con,String uid,String qid)throws SQLException{
return null!=questionDao.answer(con, uid, qid);
}
/**
* 修改一个回答
*
* @param con
* @param id
* 回答ID
* @param cnt
* 回答内容
* @param uid
* 用户ID
* @param uname
* 用户姓名
* @return
* @throws SQLException
*/
@Post
@Path("/answer/modify")
public int answerUpdate(@JdbcConn(true) Connection con, String id, String cnt, String uid, String uname) throws SQLException {
return questionDao.updateAnswer(con, id, cnt);
}
@Get
@Path("/qo")
public Question queryOne(@JdbcConn Connection con, String id) throws SQLException {
return questionDao.query(con, id);
}
@Get
@Path("/qm")
public List<Question> query(@JdbcConn Connection con, String[] ids) throws SQLException {
return questionDao.query(con, ids);
}
/**
* 等您回答 1、只显示“发布中”的问题 按问题发布时间,由新到旧排序。
*
* @param con
* @param time
* limit查询最后一条的createTime 首次不传
* @param id
* limit查询最后一条的id 首次不传
* @param rows
* limit查询最后一条的最大返回条数
* @return
* @throws SQLException
*/
@Get
@Path()
public List<Question> query(@JdbcConn Connection con, @DefaultValue("\"99999999999999\"") String time, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
return questionDao.query(con, "1", null, time+id, rows);
}
/**
* 我提出的 1、只显示“发布中”的问题 按问题发布时间,由新到旧排序。
*
* @param con
* @param uid
* 用户ID
* @param time
* limit查询最后一条的createTime 首次不传
* @param id
* limit查询最后一条的id 首次不传
* @param rows
* limit查询最后一条的最大返回条数
* @return
* @throws SQLException
*/
@Get
@Path("/my")
public List<Question> querySelf(@JdbcConn Connection con, String uid, @DefaultValue("\"99999999999999\"") String time, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
return questionDao.query(con, "1", uid, time+id, rows);
}
/**
* 我关注的 1、只显示“发布中”的问题 按问题发布时间,由新到旧排序。
*
* @param con
* @param uid
* 用户ID
* @param time
* limit查询最后一条的createTime 首次不传
* @param id
* limit查询最后一条的id 首次不传
* @param rows
* limit查询最后一条的最大返回条数
* @return
* @throws SQLException
*/
@Get
@Path("/watch")
public List<Question> watch(@JdbcConn Connection con, String uid, @DefaultValue("\"99999999999999\"") String time, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
return questionDao.watch(con, uid, time+ id, rows);
}
/**
* 我回答的
*
* @param con
* @param uid
* 用户ID
* @param time
* limit查询最后一条的createTime 首次不传
* @param id
* limit查询最后一条的id 首次不传
* @param rows
* limit查询最后一条的最大返回条数
* @return
* @throws SQLException
*/
@Get
@Path("/answer/bySelf")
public List<Answer> answerSelf(@JdbcConn Connection con, String uid, @DefaultValue("\"99999999999999\"") String time, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
return questionDao.answerSelf(con, uid, time+id, rows);
}
/**
* 我关注的回答
*
* @param con
* @param uid
* 用户ID
* @param time
* limit查询最后一条的createTime 首次不传
* @param id
* limit查询最后一条的id 首次不传
* @param rows
* limit查询最后一条的最大返回条数
* @return
* @throws SQLException
*/
@Get
@Path("/answer/byWatch")
public List<Answer> answerWatch(@JdbcConn Connection con, String uid, @DefaultValue("\"99999999999999\"") String time, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
return questionDao.watchAnswer(con, uid, time+id, rows);
}
/**
* 查询所有合法的回答(“发布中”的问题下的“发布中”的回答。);
* 按最新回答时间,由新到旧排序
})
* @param con
* @param time
* @param id
* @param rows
* @return
* @throws SQLException
*/
@Get
@Path("/answer/byTime")
public List<Answer> answerByTime(@JdbcConn Connection con,@DefaultValue("\"99999999999999\"") String time, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
return questionDao.answer(con, time+ id, rows);
}
/**
* 指定提问的回答(按最新回答时间,由新到旧排序)
*
* @param con
* @param qid
* 提问ID
* @param time
* limit查询最后一条的createTime 首次不传
* @param id
* limit查询最后一条的id 首次不传
* @param rows
* limit查询最后一条的最大返回条数
* @return
* @throws SQLException
*/
@Get
@Path("/answer/qes/byTime")
public List<Answer> byQes(@JdbcConn Connection con, String qid, @DefaultValue("\"99999999999999\"") String time, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
return questionDao.byQes(con, qid, time+id, rows);
}
/**
* 指定提问的回答(按最新回答时间,由新到旧排序)
*
* @param con
* @param qid
* 提问ID
* @param score
* limit查询最后一条的score 首次不传
* @param id
* limit查询最后一条的id 首次不传
* @param rows
* limit查询最后一条的最大返回条数
* @return
* @throws SQLException
*/
@Get
@Path("/answer/qes/byScore")
public List<SortedAnswwer> byQes(@JdbcConn Connection con, String qid, @DefaultValue("100000") int score, @DefaultValue("\"0\"") String id,
@DefaultValue("10000000") int rows) throws SQLException {
String p = "000000"+score;
p = p.substring(p.length()-5)+id;
return questionDao.byQesScore(con, qid, p, rows);
}
/**
* 我的回答总点赞数
*
* @param con
* @param id
* 用户ID
* @return
* @throws SQLException
*/
@Get
@Path("/answer/my/agree/count")
public int queryCountWithMyAnswer(@JdbcConn Connection con, final String id) throws SQLException {
return JdbcUtil.queryInt(con, "SELECT COUNT(AGREE) FROM ANSWER WHERE UID=?", new PreparedStatementConfig() {
@Override
public void config(PreparedStatement ps) throws SQLException {
ps.setString(1, id);
}
}, 0);
}
@Post
@Path("/answer/agree")
public void agree(@JdbcConn(true) Connection con, String id, String uid, String uname, @AfterCommit List<Runnable> runs)
throws SQLException, JfwBaseException {
Answer a = questionDao.queryAnswer(con, id);
if (a == null)
throw new JfwBaseException(50000, "answer[" + id + "] not found");
String qid = a.getQid();
Question q = questionDao.query(con, qid);
if (q == null)
throw new JfwBaseException(50000, "answer[" + id + "] ' question not found");
AnswerAgreeRec aar = new AnswerAgreeRec();
aar.setAid(id);
aar.setUid(uid);
aar.setFlag(true);
boolean inserted = false;
try {
questionDao.insert(con, aar);
inserted = true;
} catch (SQLException e) {
if (SqlUtil.unique(e)) {
con.rollback();
}
throw e;
}
if (inserted) {
questionDao.update(con, id, 1, 1);
} else {
if (questionDao.agree(con, uid, id) > 0) {
questionDao.update(con, id, 1, 0);
} else {
throw new JfwBaseException(50001, "duplicate answer agree");
}
}
notifyService.notify(con, a.getUid(), uid, uname, a.getId(), q.getTitle(), NotifyType.ACCEPT_ANSWER, runs);
}
@Get
@Path("/answer/delete")
public int logicDelete(@JdbcConn(true) Connection con,String id)throws SQLException{
return questionDao.logicDeleteAnswer(con, id);
}
@Post
@Path("/answer/unAgree")
public void unAgree(@JdbcConn(true) Connection con, String id, String uid, String uname, @AfterCommit List<Runnable> runs)
throws SQLException, JfwBaseException {
AnswerAgreeRec aar = new AnswerAgreeRec();
aar.setAid(id);
aar.setUid(uid);
aar.setFlag(false);
boolean inserted = false;
try {
questionDao.insert(con, aar);
inserted = true;
} catch (SQLException e) {
if (SqlUtil.unique(e)) {
con.rollback();
}
throw e;
}
if (inserted) {
questionDao.update(con, id, 0, 1);
} else {
if (questionDao.unAgree(con, uid, id) > 0) {
questionDao.update(con, id, -1, 0);
} else {
throw new JfwBaseException(50001, "duplicate answer unagree");
}
}
runs.add(new Runnable() {
@Override
public void run() {
// TODO send notify
}
});
}
public QetKeyWord[] build(String id, List<String> ss) {
List<QetKeyWord> qKws = new ArrayList<>(ss.size());
for (String s : ss) {
QetKeyWord kw = new QetKeyWord();
kw.setId(id);
kw.setKw(s);
qKws.add(kw);
}
return qKws.toArray(new QetKeyWord[qKws.size()]);
}
private void remove(List<UploadFile> list) {
for (UploadFile file : list) {
if (file.fn != null)
file.fn.delete();
}
}
private UploadFile buildTargetFile(String ext) {
StringBuilder sb = new StringBuilder();
sb.append(System.currentTimeMillis() / (1000 * 60 * 60 * 24));
File path = new File(this.imgPath, sb.toString());
if (!path.exists()) {
synchronized (this) {
if (!path.mkdirs())
throw new RuntimeException("mkdir error[" + path + "]");
FN_IDX.set(1);
}
}
File file;
do {
String fn = FN_IDX.toString();
if (ext.isEmpty()) {
file = new File(path, fn);
} else {
file = new File(path, fn + "." + ext);
}
FN_IDX.incrementAndGet();
} while (file.exists());
sb.append("/").append(file.getName());
UploadFile uf = new UploadFile();
uf.setFn(file);
uf.setUri("/" + sb.toString());
return uf;
}
private String normalizeFileName(String fn) {
int index = fn.indexOf('\\');
if (index >= 0) {
fn = fn.substring(fn.lastIndexOf('\\') + 1);
}
index = fn.indexOf('/');
if (index >= 0) {
fn = fn.substring(fn.lastIndexOf('/') + 1);
}
if (fn.length() == 0)
throw new RuntimeException("invalid filename in Multipart/data request");
return fn;
}
public static class UploadFile {
private String name;
private String uri;
private long size;
private transient File fn;
public File getFn() {
return fn;
}
public void setFn(File fn) {
this.fn = fn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
}
}
|