public abstract class API {

    public String url = null; //url
    public int access = Access.GOD_MODE; //operator's permission
    public Command authorize = null; //session check

    protected abstract Object process(HttpServletRequest req, WxSession session) throws Exception;

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{" +
                "url='" + url + '\'' +
                ", access=" + Access.inverseMap.get(access) +
                ", authorize=" + authorize +
                '}';
    }

    public static String getParameterWithDefault(String obj, String def) {
        return obj == null ? def : obj;
    }

    public static Date getParameterAsDate(String obj, Date def) {
        return obj == null ? def : new Date(Long.valueOf(obj));
    }

    public static Date getToday() {
        return DateUtils.truncate(new Date(), Calendar.DAY_OF_MONTH);
    }

    public static Date getDay(Date date) {
        return DateUtils.truncate(date, Calendar.DAY_OF_MONTH);
    }

} public class Error {

    public static final Error ALREADY_SUBMITTED = new Error(1);
    public static final Object OK = new Object();
    public static final Error PARAMETER_REQUIRED = new Error(-1);
    public static final Error ILLEGAL_PARAMETER = new Error(-2);
    //	public static final Error REQUEST_FAILED = new Error(-3); REMOVED
    public static final Error LENGTH_LIMIT_EXCEEDED = new Error(-4);
    public static final Error INVALID_PARAMETER = new Error(-5);
    public static final Error USER_NOT_FOUND = new Error(-11);
    public static final Error TICKET_NOT_FOUND = new Error(-12);
    public static final Error OPERATOR_NOT_FOUND = new Error(-13);
    public static final Error UNAUTHORIZED = new Error(-20);
    public static final Error WRONG_PASSWORD = new Error(-22);
    public static final Error PERMISSION_DENIED = new Error(-24);
    public static final Error INTERNAL_ERROR = new Error(-90);
    public static final Error DATABASE_ERROR = new Error(-91);

    public int errCode;
    public String errMsg;

    private Error(int code) {
        this(code, lang("ERR_" + code));
    }

    public Error(int errCode, String errMsg) {
        this.errCode = errCode;
        this.errMsg = errMsg;
    }

    public Error withMsg(String msg) {
        return new Error(errCode, msg);
    }

} public class Access {

    public static final int GOD_MODE = -1;
    public static final int ROOT = 0;
    public static final int MANAGER = 1;
    public static final int CO_MANAGER = 2;
    public static final int LEADER = 3;
    public static final int CO_LEADER = 4;
    public static final int ELITE = 5;
    public static final int ELDER = 6;
    public static final int MEMBER = 7;
    public static final int PRE_MEMBER = 8;
    public static final int NO_LOGIN = 9;
    public static final int USER = 10;
    public static final int GUEST = 11;

    public static final Map<Integer, String> inverseMap = new HashMap<>();

    static {
        System.out.println("Loading Access...");
        for (Field field : Access.class.getDeclaredFields()) {
            if (field.getType().isAssignableFrom(Integer.TYPE)) {
                try {
                    inverseMap.put((Integer) field.get(null), field.getName());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static String getLocalized(int access) {
        if (inverseMap.containsKey(access)) {
            return lang("ACCESS_" + inverseMap.get(access));
        }
        return null;
    }

} public class Attribute {

    public static final String AUTHORIZED = "authorized";
    public static final String WECHAT = "wechat";
    public static final String OPERATOR = "operator";
    public static final String USER = "user";

} public enum ISP {

    TELECOM(1, "^1[3|4|5|6|7|8|9][0-9]{9}$"),
    UNICOM(2, "^\\w+([-+.]\\w+)*@16900\\.gd"),
    CHINAMOBILE(3, "^1[3|4|5|6|7|8|9][0-9]{9}@139\\.gd$"),
    OTHER(4, ".*"),
    ;

    private static final Map<String, ISP> NAME_MAP = new HashMap<>();
    private static final Map<Integer, ISP> ID_MAP = new HashMap<>();

    static {
        for (ISP type : values()) {
            if (type.name != null) {
                NAME_MAP.put(type.name.toLowerCase(), type);
            }
            if (type.id > 0) {
                ID_MAP.put(type.id, type);
            }
        }
    }

    public final int id;
    public final String name;
    public final String accountRegex;

    ISP(int id, String accountRegex) {
        this.id = id;
        this.name = lang("ISP_" + name());
        this.accountRegex = accountRegex;
    }

    public static ISP fromName(String name) {
        if (name == null) {
            return null;
        }
        return NAME_MAP.get(name.toLowerCase());
    }

    public static ISP fromId(int id) {
        return ID_MAP.get(id);
    }

    @Override
    public String toString() {
        return name;
    }

} @Entity
@Table(name = "users")
public class User {

    //System Accounts
    public static User OFFICIAL_CHINA_UNICOM_XH;
    public static User OFFICIAL_CHINA_MOBILE_XH;
    public static User OFFICIAL_CHINA_MOBILE_FX;

    public static final String PROPERTY_NAME = "name";
    public static final String PROPERTY_WECHAT = "wechatId";
    public static final String PROPERTY_BLOCK = "block";

    @Id
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
    @Column(name = "name", updatable = false, nullable = false)
    private String name;
    @Convert(converter = ISPConverter.class)
    private ISP isp;
    @Column(name = "netaccount")
    private String netAccount;
    @Expose(serialize = false)
    @Column(name = "wechat")
    private String wechatId;
    private Integer block;
    private Integer room;
    private Long phone;

    public User() {
    }

    public User(Long id, String name, ISP isp, String netAccount, String wechatId, Integer block, Integer room, Long phone) {
        this.id = id;
        this.name = name;
        this.isp = isp;
        this.netAccount = netAccount;
        this.wechatId = wechatId;
        this.block = block;
        this.room = room;
        this.phone = phone;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ISP getIsp() {
        return isp;
    }

    public void setIsp(ISP isp) {
        this.isp = isp;
    }

    public String getNetAccount() {
        return netAccount;
    }

    public void setNetAccount(String netAccount) {
        this.netAccount = netAccount;
    }

    public String getWechatId() {
        return wechatId;
    }

    public void setWechatId(String wechatId) {
        this.wechatId = wechatId;
    }

    public Integer getBlock() {
        return block;
    }

    public void setBlock(Integer block) {
        this.block = block;
    }

    public Integer getRoom() {
        return room;
    }

    public void setRoom(Integer room) {
        this.room = room;
    }

    public Long getPhone() {
        return phone;
    }

    public void setPhone(Long phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", isp=" + isp +
                ", netAccount='" + netAccount + '\'' +
                ", wechatId='" + wechatId + '\'' +
                ", block=" + block +
                ", room=" + room +
                ", phone=" + phone +
                '}';
    }
} public interface WxSession {

    String getId();

    <T> T getAttribute(String name);

    Set<String> getAttributeNames();

    void setAttribute(String name, Object value);

    void removeAttribute(String name);

    void invalidate();

} @SuppressWarnings("Duplicates")
public class TableUser extends SQLCore {

    public static final String COLUMN_ID = "id";
    public static final String COLUMN_NAME = "name";
    public static final String COLUMN_ISP = "isp";
    public static final String COLUMN_NET_ACCOUNT = "netaccount";
    public static final String COLUMN_WECHAT = "wechat";
    public static final String COLUMN_BLOCK = "block";
    public static final String COLUMN_ROOM = "room";
    public static final String COLUMN_PHONE = "phone";

    public static User getById(long id) {
        try (Session s = sf.openSession()) {
            return s.get(User.class, id);
        }
    }

    public static User getByName(String name) {
        try (Session s = sf.openSession()) {
            return (User) s.createCriteria(User.class).add(Restrictions.eq(User.PROPERTY_NAME, name)).uniqueResult();
        }
    }

    public static User getByWechat(String wechat) {
        try {
            User u = cache.get(wechat);
            return u == NULL_USER ? null : u;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static int update(User user) {
        try (Session s = sf.openSession()) {
            s.beginTransaction();
            s.update(user);
            s.getTransaction().commit();
            cache.put(user.getWechatId(), user);
            return 1;
        }
    }

    protected static void init() {
        try (Session s = SQLCore.sf.openSession()) {
            User.OFFICIAL_CHINA_UNICOM_XH = s.get(User.class, 100104L);
            User.OFFICIAL_CHINA_MOBILE_XH = s.get(User.class, 100864L);
            User.OFFICIAL_CHINA_MOBILE_FX = s.get(User.class, 100865L);
        }
    }

    private static LoadingCache<String, User> cache = CacheBuilder.newBuilder()
            .concurrencyLevel(4)
            .maximumSize(4096)
            .expireAfterAccess(Settings.I.User_Wechat_Cache_Expire_Time, TimeUnit.SECONDS)
            .build(new ValueLoader());

    private static class ValueLoader extends CacheLoader<String, User> {
        @Override
        public User load(String key) throws Exception {
            User u = TableUser.getByWechat0(key);
            return u == null ? NULL_USER : u;
        }
    }

    private static final User NULL_USER = new User();

    public static void flushCache() {
        cache.invalidateAll();
    }

    private static User getByWechat0(String wechat) {
        try (Session s = sf.openSession()) {
            return (User) s.createCriteria(User.class).add(Restrictions.eq(User.PROPERTY_WECHAT, wechat)).uniqueResult();
        }
    }

} public enum Command {

    REGISTER(0, RegisterHandler.class),
    QUERY(1, QueryHandler.class),
    SUBMIT(2, SubmitHandler.class),
    CANCEL(3, CancelHandler.class),
    PROFILE(4, ProfileHandler.class),
    LOGIN(10, LoginHandler.class),
    OPERATOR_INFO(11, OperatorInfoHandler.class),
    SIGN(12, SignHandler.class), //FIXME
    ;

    private static final Map<Integer, Command> ID_MAP = new HashMap<>();

    static {
        for (Command type : values()) {
            if (type.id >= 0) {
                ID_MAP.put(type.id, type);
            }
        }
    }

    public final String regex;
    public final Class<? extends WxMpMessageHandler> handler;
    public final int id;

    Command(int id, Class<? extends WxMpMessageHandler> handler) {
        this.id = id;
        this.regex = lang("REGEX_" + name());
        this.handler = handler;
    }

    public static Command fromId(int id) {
        return ID_MAP.get(id);
    }

    @Override
    public String toString() {
        return name();
    }

} public static int checkBlock(String block) {
    if (block == null) return -1;
    try {
        int b = Integer.parseInt(block);
        if (Block.inverseMap.containsKey(b))
            return b;
        else
            return -1;
    } catch (NumberFormatException ignored) {
    }
    return -1;
} public static ISP checkISP(String isp) {
    if (isp == null) return null;
    try {
        return ISP.fromId(Integer.parseInt(isp));
    } catch (NumberFormatException ignored) {
    }
    return null;
} public static String checkNetAccount(String account, ISP isp) {
    if (isp == null) return null;
    if (account == null) return null;
    if (!account.matches(isp.accountRegex)) return null;
    return account;
} public static long checkPhoneNumber(String phone) {
    if (phone == null) return -1;
    if (!phone.matches(PHONE_NUMBER_REGEX)) return -1;
    try {
        return Long.parseLong(phone);
    } catch (NumberFormatException ignored) {
    }
    return -1;
} public static int checkRoom(String room, int block) {
    if (block == -1) return -1;
    if (room == null) return -1;
    try {
        Integer i = Integer.parseInt(room);
        if (Block.checkRoom(block, i))
            return i;
        else
            return -1;
    } catch (NumberFormatException ignored) {
    }
    return -1;
} public static long checkStudentId(String studentId) {
    if (studentId == null) return -1;
    if (studentId.matches(STUDENT_ID_REGEX)) {
        try {
            return Long.parseLong(studentId);
        } catch (NumberFormatException ignored) {
        }
    }
    return -1;
}
import static love.sola.netsupport.util.Checker.checkBlock;
import static love.sola.netsupport.util.Checker.checkISP;
import static love.sola.netsupport.util.Checker.checkNetAccount;
import static love.sola.netsupport.util.Checker.checkPhoneNumber;
import static love.sola.netsupport.util.Checker.checkRoom;
import static love.sola.netsupport.util.Checker.checkStudentId;
import org.hibernate.exception.ConstraintViolationException;
import javax.servlet.http.HttpServletRequest;
import love.sola.netsupport.api.API;
import love.sola.netsupport.api.Error;
import love.sola.netsupport.enums.Access;
import love.sola.netsupport.enums.Attribute;
import love.sola.netsupport.enums.ISP;
import love.sola.netsupport.pojo.User;
import love.sola.netsupport.session.WxSession;
import love.sola.netsupport.sql.TableUser;
import love.sola.netsupport.wechat.Command;
/*
 * This file is part of WechatTicketSystem.
 *
 * WechatTicketSystem is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WechatTicketSystem is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with WechatTicketSystem.  If not, see <http://www.gnu.org/licenses/>.
 */

package love.sola.netsupport.api.user;





/**
 * @author Sola {@literal <dev@sola.love>}
 */
public class Register extends API {

    public Register() {
        url = "/register";
        access = Access.GUEST;
        authorize = Command.REGISTER;
    }

    @Override
    protected Object process(HttpServletRequest req, WxSession session) throws Exception {
        String wechat = session.getAttribute(Attribute.WECHAT);