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";

} @Entity
@Table(name = "operators")
public class Operator {

    public static final String PROPERTY_WECHAT = "wechat";

    //System Accounts
    public static Operator USER_SELF;
    public static Operator ADMIN;

    @Id
    @Column(name = "id", nullable = false, insertable = false, updatable = false)
    private Integer id;
    @Column(name = "name", nullable = false, insertable = false, updatable = false)
    private String name;
    @Column(name = "access", nullable = false, insertable = false, updatable = false)
    private Integer access;
    @Column(name = "wechat", insertable = false, updatable = false)
    @Expose(serialize = false)
    private String wechat;
    private Integer block;
    private Integer week;
    @Expose(serialize = false)
    private String password;

    public Operator(Integer id, String name, Integer access, String wechat, Integer block, Integer week, String password) {
        this.id = id;
        this.name = name;
        this.access = access;
        this.wechat = wechat;
        this.block = block;
        this.week = week;
        this.password = password;
    }

    public Operator() {
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAccess() {
        return access;
    }

    public void setAccess(Integer access) {
        this.access = access;
    }

    public String getWechat() {
        return wechat;
    }

    public void setWechat(String wechat) {
        this.wechat = wechat;
    }

    public Integer getBlock() {
        return block;
    }

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

    public Integer getWeek() {
        return week;
    }

    public void setWeek(Integer week) {
        this.week = week;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "Operator{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", access=" + access +
                ", wechat='" + wechat + '\'' +
                ", block=" + block +
                ", week=" + week +
                '}';
    }
} @Entity
@Table(name = "tickets")
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
public class Ticket {

    public static final String PROPERTY_USER = "user";
    public static final String PROPERTY_STATUS = "status";
    public static final String PROPERTY_SUBMIT_TIME = "submitTime";

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @ManyToOne(optional = false)
    @JoinColumn(name = TableTicket.COLUMN_SID)
    private User user;
    private String description;
    @Column(name = TableTicket.COLUMN_SUBMIT_TIME, insertable = false, updatable = false)
    private Date submitTime;
    private String remark;
    private Date updateTime;
    @ManyToOne
    @JoinColumn(name = TableTicket.COLUMN_OPSID)
    private Operator operator;
    private Integer status;

    public Ticket() {
    }

    public Ticket(User user, String description, Date submitTime, String remark, Date updateTime, Operator operator, Integer status) {
        this.user = user;
        this.description = description;
        this.submitTime = submitTime;
        this.remark = remark;
        this.updateTime = updateTime;
        this.operator = operator;
        this.status = status;
    }

    public Integer getId() {
        return id;
    }

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

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Date getSubmitTime() {
        return submitTime;
    }

    public void setSubmitTime(Date submitTime) {
        this.submitTime = submitTime;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    public Operator getOperator() {
        return operator;
    }

    public void setOperator(Operator operator) {
        this.operator = operator;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "Ticket{" +
                "id=" + id +
                ", user=" + user +
                ", description='" + description + '\'' +
                ", submitTime=" + submitTime +
                ", remark='" + remark + '\'' +
                ", updateTime=" + updateTime +
                ", operator=" + operator +
                ", status=" + status +
                '}';
    }
} 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();

} public class SQLCore {

    public static InitialContext ic;
    public static DataSource ds;
    public static SessionFactory sf;
    public static ServiceRegistry sr;
    public static Gson gson = new GsonBuilder()
            .addSerializationExclusionStrategy(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                    final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                    return expose != null && !expose.serialize();
                }

                @Override
                public boolean shouldSkipClass(Class<?> aClass) {
                    return false;
                }
            })
            .addDeserializationExclusionStrategy(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                    final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                    return expose != null && !expose.deserialize();
                }

                @Override
                public boolean shouldSkipClass(Class<?> aClass) {
                    return false;
                }
            })
            .registerTypeAdapter(Date.class, (JsonDeserializer<Date>) (json, typeOfT, context) -> new Date(json.getAsJsonPrimitive().getAsLong()))
            .registerTypeAdapter(Date.class, (JsonSerializer<Date>) (src, typeOfSrc, context) -> new JsonPrimitive(src.getTime()))
            .registerTypeAdapter(ISP.class, (JsonDeserializer<ISP>) (json, typeOfT, context) -> ISP.fromId(json.getAsJsonPrimitive().getAsInt()))
            .registerTypeAdapter(ISP.class, (JsonSerializer<ISP>) (src, typeOfSrc, context) -> new JsonPrimitive(src.id))
            .registerTypeAdapter(Command.class, (JsonDeserializer<Command>) (json, typeOfT, context) -> Command.fromId(json.getAsJsonPrimitive().getAsInt()))
            .registerTypeAdapter(Command.class, (JsonSerializer<Command>) (src, typeOfSrc, context) -> new JsonPrimitive(src.id))
            .registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY)
            .create();

    static {
        try {
            ic = new InitialContext();
            ds = (DataSource) ic.lookup("java:comp/env/jdbc/netsupport");
            ds.setLoginTimeout(3);

            sr = new StandardServiceRegistryBuilder().configure().build();
            sf = new MetadataSources(sr).buildMetadata().buildSessionFactory();

            TableUser.init();
            TableOperator.init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void destroy() {
        try {
            SQLCore.sf.close();
            ((ComboPooledDataSource) SQLCore.ds).close();
            SQLCore.ic.close();
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    public static AuditReader getAuditReader(Session session) {
        return AuditReaderFactory.get(session);
    }

    public static class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> {

        public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
            @Override
            @SuppressWarnings("unchecked")
            public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
                return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null);
            }
        };
        private final Gson context;

        private HibernateProxyTypeAdapter(Gson context) {
            this.context = context;
        }

        @Override
        public HibernateProxy read(JsonReader in) throws IOException {
            throw new UnsupportedOperationException("Not supported");
        }

        @SuppressWarnings({"rawtypes", "unchecked"})
        @Override
        public void write(JsonWriter out, HibernateProxy value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            // Retrieve the original (not proxy) class
            Class<?> baseType = Hibernate.getClass(value);
            // Get the TypeAdapter of the original class, to delegate the serialization
            TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
            // Get a filled instance of the original class
            Object unproxiedValue = value.getHibernateLazyInitializer()
                    .getImplementation();
            // Serialize the value
            delegate.write(out, unproxiedValue);
        }
    }

} public class Checker {

    public static final String STUDENT_ID_REGEX = "^(2|1)[0-9]{9,12}$";
    public static final String PHONE_NUMBER_REGEX = "^1[3456789][0-9]{9}$";

    public static boolean hasNull(Object... v) {
        for (Object o : v) if (o == null) return true;
        return false;
    }

    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;
    }

    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 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 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 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 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();
    }

}
import love.sola.netsupport.sql.SQLCore;
import love.sola.netsupport.util.Checker;
import love.sola.netsupport.wechat.Command;
import org.hibernate.Session;
import java.util.Date;
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.pojo.Operator;
import love.sola.netsupport.pojo.Ticket;
import love.sola.netsupport.session.WxSession;
/*
 * 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.stuff;





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

    public TicketUpdate() {
        url = "/admin/ticketupdate";
        access = Access.MEMBER;
        authorize = Command.LOGIN;